mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-17 20:44:38 +03:00
improve ui
This commit is contained in:
@@ -89,15 +89,17 @@ set(PROJECT_SOURCES
|
|||||||
3rdparty/qrcodegen.cpp
|
3rdparty/qrcodegen.cpp
|
||||||
3rdparty/QtExtKeySequenceEdit.cpp
|
3rdparty/QtExtKeySequenceEdit.cpp
|
||||||
|
|
||||||
qv2ray/ui/LogHighlighter.cpp
|
qv2ray/v2/ui/LogHighlighter.cpp
|
||||||
qv2ray/ui/QvAutoCompleteTextEdit.cpp
|
qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp
|
||||||
qv2ray/utils/HTTPRequestHelper.cpp
|
qv2ray/v2/utils/HTTPRequestHelper.cpp
|
||||||
qv2ray/components/proxy/QvProxyConfigurator.cpp
|
qv2ray/v2/components/proxy/QvProxyConfigurator.cpp
|
||||||
qv2ray/ui/widgets/common/QJsonModel.cpp
|
qv2ray/v2/ui/widgets/common/QJsonModel.cpp
|
||||||
|
qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp
|
||||||
|
qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp
|
||||||
|
qv2ray/v2/ui/widgets/editors/w_JsonEditor.ui
|
||||||
|
|
||||||
qv2ray/ui/widgets/editors/w_JsonEditor.cpp
|
qv2ray/v3/components/GeositeReader/GeositeReader.cpp
|
||||||
qv2ray/ui/widgets/editors/w_JsonEditor.hpp
|
qv2ray/v3/components/GeositeReader/picoproto.cpp
|
||||||
qv2ray/ui/widgets/editors/w_JsonEditor.ui
|
|
||||||
|
|
||||||
rpc/gRPC.cpp
|
rpc/gRPC.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "qv2ray/wrapper.hpp"
|
#include "qv2ray/wrapper.hpp"
|
||||||
#include "qv2ray/ui/widgets/common/QJsonModel.hpp"
|
#include "qv2ray/v2/ui/widgets/common/QJsonModel.hpp"
|
||||||
#include "ui_w_JsonEditor.h"
|
#include "ui_w_JsonEditor.h"
|
||||||
|
|
||||||
|
|
||||||
42
qv2ray/v3/components/GeositeReader/GeositeReader.cpp
Normal file
42
qv2ray/v3/components/GeositeReader/GeositeReader.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include "GeositeReader.hpp"
|
||||||
|
|
||||||
|
#include "qv2ray/wrapper.hpp"
|
||||||
|
#include "picoproto.hpp"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
namespace Qv2ray::components::GeositeReader {
|
||||||
|
QMap<QString, QStringList> GeositeEntries;
|
||||||
|
|
||||||
|
QStringList ReadGeoSiteFromFile(const QString &filepath, bool allowCache) {
|
||||||
|
if (GeositeEntries.contains(filepath) && allowCache)
|
||||||
|
return GeositeEntries.value(filepath);
|
||||||
|
|
||||||
|
QStringList list;
|
||||||
|
qInfo() << "Reading geosites from:" << filepath;
|
||||||
|
QFile f(filepath);
|
||||||
|
bool opened = f.open(QFile::OpenModeFlag::ReadOnly);
|
||||||
|
|
||||||
|
if (!opened) {
|
||||||
|
qInfo() << "File cannot be opened:" << filepath;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto content = f.readAll();
|
||||||
|
f.close();
|
||||||
|
{
|
||||||
|
picoproto::Message root;
|
||||||
|
root.ParseFromBytes((unsigned char *) content.data(), content.size());
|
||||||
|
|
||||||
|
list.reserve(root.GetMessageArray(1).size());
|
||||||
|
for (const auto &geosite: root.GetMessageArray(1))
|
||||||
|
list << QString::fromStdString(geosite->GetString(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
qInfo() << "Loaded" << list.count() << "geosite entries from data file.";
|
||||||
|
list.sort();
|
||||||
|
GeositeEntries[filepath] = list;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
} // namespace Qv2ray::components::geosite
|
||||||
7
qv2ray/v3/components/GeositeReader/GeositeReader.hpp
Normal file
7
qv2ray/v3/components/GeositeReader/GeositeReader.hpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace Qv2ray::components::GeositeReader {
|
||||||
|
QStringList ReadGeoSiteFromFile(const QString &filepath, bool allowCache = true);
|
||||||
|
} // namespace Qv2ray::components::GeositeReader
|
||||||
664
qv2ray/v3/components/GeositeReader/picoproto.cpp
Normal file
664
qv2ray/v3/components/GeositeReader/picoproto.cpp
Normal file
@@ -0,0 +1,664 @@
|
|||||||
|
/* Copyright 2016 Pete Warden. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#include "picoproto.hpp"
|
||||||
|
|
||||||
|
namespace picoproto
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
// To keep the dependencies down, here's a local copy of the widespread bit_cast
|
||||||
|
// operator. This is necessary because in practice weird things can happen if
|
||||||
|
// you just try to use reinterpret_cast.
|
||||||
|
template<class Dest, class Source>
|
||||||
|
inline Dest bit_cast(const Source &source)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(Dest) == sizeof(Source), "Sizes do not match");
|
||||||
|
Dest dest;
|
||||||
|
memcpy(&dest, &source, sizeof(dest));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are defined in:
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/encoding
|
||||||
|
enum WireType
|
||||||
|
{
|
||||||
|
WIRETYPE_VARINT = 0,
|
||||||
|
WIRETYPE_64BIT = 1,
|
||||||
|
WIRETYPE_LENGTH_DELIMITED = 2,
|
||||||
|
WIRETYPE_GROUP_START = 3,
|
||||||
|
WIRETYPE_GROUP_END = 4,
|
||||||
|
WIRETYPE_32BIT = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pull bytes from the stream, updating the state.
|
||||||
|
bool ConsumeBytes(uint8_t **current, size_t how_many, size_t *remaining)
|
||||||
|
{
|
||||||
|
if (how_many > *remaining)
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "ReadBytes overrun!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*current += how_many;
|
||||||
|
*remaining -= how_many;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grabs a particular type from the byte stream.
|
||||||
|
template<class T>
|
||||||
|
T ReadFromBytes(uint8_t **current, size_t *remaining)
|
||||||
|
{
|
||||||
|
PP_CHECK(ConsumeBytes(current, sizeof(T), remaining));
|
||||||
|
const T result = *(bit_cast<T *>(*current - sizeof(T)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ReadVarInt(uint8_t **current, size_t *remaining)
|
||||||
|
{
|
||||||
|
uint64_t result = 0;
|
||||||
|
bool keep_going;
|
||||||
|
int shift = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const uint8_t next_number = ReadFromBytes<uint8_t>(current, remaining);
|
||||||
|
keep_going = (next_number >= 128);
|
||||||
|
result += (uint64_t)(next_number & 0x7f) << shift;
|
||||||
|
shift += 7;
|
||||||
|
} while (keep_going);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadWireTypeAndFieldNumber(uint8_t **current, size_t *remaining, uint8_t *wire_type, uint32_t *field_number)
|
||||||
|
{
|
||||||
|
uint64_t wire_type_and_field_number = ReadVarInt(current, remaining);
|
||||||
|
*wire_type = wire_type_and_field_number & 0x07;
|
||||||
|
*field_number = wire_type_and_field_number >> 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::string FieldTypeDebugString(enum FieldType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case FIELD_UNSET: return "UNSET"; break;
|
||||||
|
case FIELD_UINT32: return "UINT32"; break;
|
||||||
|
case FIELD_UINT64: return "UINT64"; break;
|
||||||
|
case FIELD_BYTES: return "BYTES"; break;
|
||||||
|
default: return "Unknown field type"; break;
|
||||||
|
}
|
||||||
|
return "Should never get here";
|
||||||
|
}
|
||||||
|
|
||||||
|
Field::Field(FieldType type, bool owns_data) : type(type), owns_data(owns_data)
|
||||||
|
{
|
||||||
|
cached_messages = nullptr;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case FIELD_UINT32:
|
||||||
|
{
|
||||||
|
value.v_uint32 = new std::vector<uint32_t>();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FIELD_UINT64:
|
||||||
|
{
|
||||||
|
value.v_uint64 = new std::vector<uint64_t>();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FIELD_BYTES:
|
||||||
|
{
|
||||||
|
value.v_bytes = new std::vector<std::pair<uint8_t *, size_t>>();
|
||||||
|
cached_messages = new std::vector<Message *>();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Bad field type when constructing field: " << type;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Field::Field(const Field &other) : type(other.type), owns_data(other.owns_data)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case FIELD_UINT32:
|
||||||
|
{
|
||||||
|
value.v_uint32 = new std::vector<uint32_t>(*other.value.v_uint32);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FIELD_UINT64:
|
||||||
|
{
|
||||||
|
value.v_uint64 = new std::vector<uint64_t>(*other.value.v_uint64);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FIELD_BYTES:
|
||||||
|
{
|
||||||
|
if (owns_data)
|
||||||
|
{
|
||||||
|
value.v_bytes = new std::vector<std::pair<uint8_t *, size_t>>();
|
||||||
|
for (std::pair<uint8_t *, size_t> data_info : *other.value.v_bytes)
|
||||||
|
{
|
||||||
|
uint8_t *new_data = new uint8_t[data_info.second];
|
||||||
|
std::copy_n(data_info.first, data_info.second, new_data);
|
||||||
|
value.v_bytes->push_back({ new_data, data_info.second });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value.v_bytes = new std::vector<std::pair<uint8_t *, size_t>>(*other.value.v_bytes);
|
||||||
|
}
|
||||||
|
cached_messages = new std::vector<Message *>();
|
||||||
|
cached_messages->reserve(other.cached_messages->size());
|
||||||
|
for (Message *other_cached_message : *other.cached_messages)
|
||||||
|
{
|
||||||
|
Message *cached_message;
|
||||||
|
if (other_cached_message)
|
||||||
|
{
|
||||||
|
cached_message = new Message(*other_cached_message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cached_message = nullptr;
|
||||||
|
}
|
||||||
|
cached_messages->push_back(cached_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Bad field type when constructing field: " << type;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Field::~Field()
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case FIELD_UINT32: delete value.v_uint32; break;
|
||||||
|
case FIELD_UINT64: delete value.v_uint64; break;
|
||||||
|
case FIELD_BYTES:
|
||||||
|
{
|
||||||
|
if (owns_data)
|
||||||
|
for (std::pair<uint8_t *, size_t> data_info : *value.v_bytes)
|
||||||
|
delete[] data_info.first;
|
||||||
|
delete value.v_bytes;
|
||||||
|
|
||||||
|
for (Message *cached_message : *cached_messages)
|
||||||
|
if (cached_message)
|
||||||
|
delete cached_message;
|
||||||
|
delete cached_messages;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: PP_LOG(ERROR) << "Bad field type when destroying field: " << type; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::Message() : Message(true){};
|
||||||
|
|
||||||
|
Message::Message(bool copy_arrays) : copy_arrays(copy_arrays){};
|
||||||
|
|
||||||
|
Message::Message(const Message &other) : field_map(other.field_map), fields(other.fields), copy_arrays(other.copy_arrays){};
|
||||||
|
|
||||||
|
Message::~Message(){};
|
||||||
|
|
||||||
|
bool Message::ParseFromBytes(uint8_t *bytes, size_t bytes_size)
|
||||||
|
{
|
||||||
|
uint8_t *current = bytes;
|
||||||
|
size_t remaining = bytes_size;
|
||||||
|
while (remaining > 0)
|
||||||
|
{
|
||||||
|
uint8_t wire_type;
|
||||||
|
uint32_t field_number;
|
||||||
|
ReadWireTypeAndFieldNumber(¤t, &remaining, &wire_type, &field_number);
|
||||||
|
switch (wire_type)
|
||||||
|
{
|
||||||
|
case WIRETYPE_VARINT:
|
||||||
|
{
|
||||||
|
Field *field = AddField(field_number, FIELD_UINT64);
|
||||||
|
const uint64_t varint = ReadVarInt(¤t, &remaining);
|
||||||
|
field->value.v_uint64->push_back(varint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WIRETYPE_64BIT:
|
||||||
|
{
|
||||||
|
Field *field = AddField(field_number, FIELD_UINT64);
|
||||||
|
const uint64_t value = ReadFromBytes<uint64_t>(¤t, &remaining);
|
||||||
|
field->value.v_uint64->push_back(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WIRETYPE_LENGTH_DELIMITED:
|
||||||
|
{
|
||||||
|
Field *field = AddField(field_number, FIELD_BYTES);
|
||||||
|
const uint64_t size = ReadVarInt(¤t, &remaining);
|
||||||
|
uint8_t *data;
|
||||||
|
if (copy_arrays)
|
||||||
|
{
|
||||||
|
data = new uint8_t[size];
|
||||||
|
std::copy_n(current, size, data);
|
||||||
|
field->owns_data = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = current;
|
||||||
|
field->owns_data = false;
|
||||||
|
}
|
||||||
|
field->value.v_bytes->push_back({ data, size });
|
||||||
|
field->cached_messages->push_back(nullptr);
|
||||||
|
current += size;
|
||||||
|
remaining -= size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WIRETYPE_GROUP_START:
|
||||||
|
{
|
||||||
|
PP_LOG(INFO) << field_number << ": GROUPSTART" << std::endl;
|
||||||
|
PP_LOG(ERROR) << "Unhandled wire type encountered";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WIRETYPE_GROUP_END:
|
||||||
|
{
|
||||||
|
PP_LOG(INFO) << field_number << ": GROUPEND" << std::endl;
|
||||||
|
PP_LOG(ERROR) << "Unhandled wire type encountered";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WIRETYPE_32BIT:
|
||||||
|
{
|
||||||
|
Field *field = AddField(field_number, FIELD_UINT32);
|
||||||
|
const uint32_t value = ReadFromBytes<uint32_t>(¤t, &remaining);
|
||||||
|
field->value.v_uint32->push_back(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Unknown wire type encountered: " << static_cast<int>(wire_type) << " at offset" << (bytes_size - remaining);
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Field *Message::AddField(int32_t number, enum FieldType type)
|
||||||
|
{
|
||||||
|
Field *field = GetField(number);
|
||||||
|
if (!field)
|
||||||
|
{
|
||||||
|
fields.push_back(Field(type, copy_arrays));
|
||||||
|
field = &fields.back();
|
||||||
|
field_map.insert({ number, fields.size() - 1 });
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
Field *Message::GetField(int32_t number)
|
||||||
|
{
|
||||||
|
if (field_map.count(number) == 0)
|
||||||
|
return nullptr;
|
||||||
|
return &(fields[field_map[number]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Field *Message::GetFieldAndCheckType(int32_t number, enum FieldType type)
|
||||||
|
{
|
||||||
|
Field *field = GetField(number);
|
||||||
|
PP_CHECK(field) << "No field for " << number;
|
||||||
|
PP_CHECK(field->type == type) << "For field " << number << " wanted type " << FieldTypeDebugString(type) << " but found " << FieldTypeDebugString(field->type);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t Message::GetInt32(int32_t number)
|
||||||
|
{
|
||||||
|
Field *field = GetFieldAndCheckType(number, FIELD_UINT32);
|
||||||
|
uint32_t first_value = (*(field->value.v_uint32))[0];
|
||||||
|
int32_t zig_zag_decoded = static_cast<int32_t>((first_value >> 1) ^ (-(first_value & 1)));
|
||||||
|
return zig_zag_decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t Message::GetInt64(int32_t number)
|
||||||
|
{
|
||||||
|
Field *field = GetFieldAndCheckType(number, FIELD_UINT64);
|
||||||
|
uint64_t first_value = (*(field->value.v_uint64))[0];
|
||||||
|
int64_t zig_zag_decoded = static_cast<int64_t>((first_value >> 1) ^ (-(first_value & 1)));
|
||||||
|
return zig_zag_decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Message::GetUInt32(int32_t number)
|
||||||
|
{
|
||||||
|
Field *field = GetFieldAndCheckType(number, FIELD_UINT32);
|
||||||
|
uint32_t first_value = (*(field->value.v_uint32))[0];
|
||||||
|
return first_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Message::GetUInt64(int32_t number)
|
||||||
|
{
|
||||||
|
Field *field = GetFieldAndCheckType(number, FIELD_UINT64);
|
||||||
|
uint64_t first_value = (*(field->value.v_uint64))[0];
|
||||||
|
return first_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t Message::GetInt(int32_t number)
|
||||||
|
{
|
||||||
|
Field *field = GetField(number);
|
||||||
|
PP_CHECK(field) << "No field for " << number;
|
||||||
|
PP_CHECK((field->type == FIELD_UINT32) || (field->type == FIELD_UINT64))
|
||||||
|
<< "For field " << number << " wanted integer type but found " << FieldTypeDebugString(field->type);
|
||||||
|
switch (field->type)
|
||||||
|
{
|
||||||
|
case FIELD_UINT32: return GetInt32(number); break;
|
||||||
|
case FIELD_UINT64: return GetInt64(number); break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Should never get here.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Should never get here.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Message::GetBool(int32_t number)
|
||||||
|
{
|
||||||
|
return (GetInt(number) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Message::GetFloat(int32_t number)
|
||||||
|
{
|
||||||
|
uint32_t int_value = GetUInt32(number);
|
||||||
|
float float_value = *(bit_cast<float *>(&int_value));
|
||||||
|
return float_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Message::GetDouble(int32_t number)
|
||||||
|
{
|
||||||
|
uint64_t int_value = GetUInt64(number);
|
||||||
|
return *(bit_cast<double *>(&int_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<uint8_t *, size_t> Message::GetBytes(int32_t number)
|
||||||
|
{
|
||||||
|
Field *field = GetFieldAndCheckType(number, FIELD_BYTES);
|
||||||
|
std::pair<uint8_t *, size_t> first_value = (*(field->value.v_bytes))[0];
|
||||||
|
return first_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Message::GetString(int32_t number)
|
||||||
|
{
|
||||||
|
Field *field = GetFieldAndCheckType(number, FIELD_BYTES);
|
||||||
|
std::pair<uint8_t *, size_t> first_value = (*(field->value.v_bytes))[0];
|
||||||
|
std::string result(first_value.first, first_value.first + first_value.second);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message *Message::GetMessage(int32_t number)
|
||||||
|
{
|
||||||
|
Field *field = GetFieldAndCheckType(number, FIELD_BYTES);
|
||||||
|
Message *cached_message = field->cached_messages->at(0);
|
||||||
|
if (!cached_message)
|
||||||
|
{
|
||||||
|
std::pair<uint8_t *, size_t> first_value = (*(field->value.v_bytes))[0];
|
||||||
|
cached_message = new Message(copy_arrays);
|
||||||
|
cached_message->ParseFromBytes(first_value.first, first_value.second);
|
||||||
|
field->cached_messages->at(0) = cached_message;
|
||||||
|
}
|
||||||
|
return cached_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int32_t> Message::GetInt32Array(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> raw_array = GetUInt64Array(number);
|
||||||
|
std::vector<int32_t> result;
|
||||||
|
result.reserve(raw_array.size());
|
||||||
|
for (uint64_t raw_value : raw_array)
|
||||||
|
{
|
||||||
|
int32_t zig_zag_decoded = static_cast<int32_t>((raw_value >> 1) ^ (-(raw_value & 1)));
|
||||||
|
result.push_back(zig_zag_decoded);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int64_t> Message::GetInt64Array(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> raw_array = GetUInt64Array(number);
|
||||||
|
std::vector<int64_t> result;
|
||||||
|
result.reserve(raw_array.size());
|
||||||
|
for (uint64_t raw_value : raw_array)
|
||||||
|
{
|
||||||
|
int64_t zig_zag_decoded = static_cast<int64_t>((raw_value >> 1) ^ (-(raw_value & 1)));
|
||||||
|
result.push_back(zig_zag_decoded);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> Message::GetUInt32Array(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> raw_array = GetUInt64Array(number);
|
||||||
|
std::vector<uint32_t> result;
|
||||||
|
result.reserve(raw_array.size());
|
||||||
|
for (uint64_t raw_value : raw_array)
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<uint32_t>(raw_value));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint64_t> Message::GetUInt64Array(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> result;
|
||||||
|
Field *field = GetField(number);
|
||||||
|
if (!field)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (field->type == FIELD_UINT64)
|
||||||
|
{
|
||||||
|
result.reserve(field->value.v_uint64->size());
|
||||||
|
for (uint64_t value : *field->value.v_uint64)
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<uint64_t>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (field->type == FIELD_UINT32)
|
||||||
|
{
|
||||||
|
result.reserve(field->value.v_uint32->size());
|
||||||
|
for (uint32_t value : *field->value.v_uint32)
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<uint64_t>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (field->type == FIELD_BYTES)
|
||||||
|
{
|
||||||
|
for (std::pair<uint8_t *, size_t> data_info : *field->value.v_bytes)
|
||||||
|
{
|
||||||
|
uint8_t *current = data_info.first;
|
||||||
|
size_t remaining = data_info.second;
|
||||||
|
while (remaining > 0)
|
||||||
|
{
|
||||||
|
const uint64_t varint = ReadVarInt(¤t, &remaining);
|
||||||
|
result.push_back(static_cast<int64_t>(varint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Expected field type UINT32, UINT64, or BYTES but got " << FieldTypeDebugString(field->type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<bool> Message::GetBoolArray(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> raw_array = GetUInt64Array(number);
|
||||||
|
std::vector<bool> result;
|
||||||
|
result.reserve(raw_array.size());
|
||||||
|
for (uint64_t raw_value : raw_array)
|
||||||
|
{
|
||||||
|
result.push_back(raw_value != 0);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<float> Message::GetFloatArray(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<float> result;
|
||||||
|
Field *field = GetField(number);
|
||||||
|
if (!field)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (field->type == FIELD_UINT32)
|
||||||
|
{
|
||||||
|
result.reserve(field->value.v_uint32->size());
|
||||||
|
for (uint32_t value : *field->value.v_uint32)
|
||||||
|
{
|
||||||
|
result.push_back(bit_cast<float>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (field->type == FIELD_BYTES)
|
||||||
|
{
|
||||||
|
for (std::pair<uint8_t *, size_t> data_info : *field->value.v_bytes)
|
||||||
|
{
|
||||||
|
uint8_t *current = data_info.first;
|
||||||
|
size_t remaining = data_info.second;
|
||||||
|
while (remaining > 0)
|
||||||
|
{
|
||||||
|
const uint64_t varint = ReadVarInt(¤t, &remaining);
|
||||||
|
const uint32_t varint32 = static_cast<uint32_t>(varint & 0xffffffff);
|
||||||
|
result.push_back(bit_cast<float>(varint32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Expected field type UINT32 or BYTES but got " << FieldTypeDebugString(field->type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> Message::GetDoubleArray(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<double> result;
|
||||||
|
Field *field = GetField(number);
|
||||||
|
if (!field)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (field->type == FIELD_UINT64)
|
||||||
|
{
|
||||||
|
result.reserve(field->value.v_uint64->size());
|
||||||
|
for (uint64_t value : *field->value.v_uint64)
|
||||||
|
{
|
||||||
|
result.push_back(bit_cast<double>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (field->type == FIELD_BYTES)
|
||||||
|
{
|
||||||
|
for (std::pair<uint8_t *, size_t> data_info : *field->value.v_bytes)
|
||||||
|
{
|
||||||
|
uint8_t *current = data_info.first;
|
||||||
|
size_t remaining = data_info.second;
|
||||||
|
while (remaining > 0)
|
||||||
|
{
|
||||||
|
const uint64_t varint = ReadVarInt(¤t, &remaining);
|
||||||
|
result.push_back(bit_cast<double>(varint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Expected field type UINT64 or BYTES but got " << FieldTypeDebugString(field->type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<uint8_t *, size_t>> Message::GetByteArray(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<uint8_t *, size_t>> result;
|
||||||
|
Field *field = GetField(number);
|
||||||
|
if (!field)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (field->type == FIELD_BYTES)
|
||||||
|
{
|
||||||
|
result.reserve(field->value.v_bytes->size());
|
||||||
|
for (std::pair<uint8_t *, size_t> data_info : *field->value.v_bytes)
|
||||||
|
{
|
||||||
|
result.push_back(data_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Message::GetStringArray(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<std::string> result;
|
||||||
|
Field *field = GetField(number);
|
||||||
|
if (!field)
|
||||||
|
return result;
|
||||||
|
if (field->type == FIELD_BYTES)
|
||||||
|
{
|
||||||
|
result.reserve(field->value.v_bytes->size());
|
||||||
|
for (std::pair<uint8_t *, size_t> data_info : *field->value.v_bytes)
|
||||||
|
{
|
||||||
|
result.push_back(std::string(data_info.first, data_info.first + data_info.second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Message *> Message::GetMessageArray(int32_t number)
|
||||||
|
{
|
||||||
|
std::vector<Message *> result;
|
||||||
|
Field *field = GetField(number);
|
||||||
|
if (!field)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (field->type == FIELD_BYTES)
|
||||||
|
{
|
||||||
|
result.reserve(field->value.v_bytes->size());
|
||||||
|
for (size_t i = 0; i < field->value.v_bytes->size(); ++i)
|
||||||
|
{
|
||||||
|
Message *cached_message = field->cached_messages->at(i);
|
||||||
|
if (!cached_message)
|
||||||
|
{
|
||||||
|
std::pair<uint8_t *, size_t> value = field->value.v_bytes->at(i);
|
||||||
|
cached_message = new Message(copy_arrays);
|
||||||
|
cached_message->ParseFromBytes(value.first, value.second);
|
||||||
|
field->cached_messages->at(i) = cached_message;
|
||||||
|
}
|
||||||
|
result.push_back(cached_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PP_LOG(ERROR) << "Expected field type BYTES but got " << FieldTypeDebugString(field->type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace picoproto
|
||||||
213
qv2ray/v3/components/GeositeReader/picoproto.hpp
Normal file
213
qv2ray/v3/components/GeositeReader/picoproto.hpp
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/* Copyright 2016 Pete Warden. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
See the README for full details, but this module lets you read in protobuf
|
||||||
|
encoded files with a minimal code footprint.
|
||||||
|
|
||||||
|
It doesn't create classes for each kind of message it encounters, it just has a
|
||||||
|
single Message interface that lets you access the members of the protobuf as a
|
||||||
|
key/value store. This loses a lot of the convenience of type-checked classes,
|
||||||
|
but it does mean that very little code is needed to access data from files.
|
||||||
|
|
||||||
|
As a simple example, if you had read a `bytes_size` long file into `bytes` that
|
||||||
|
contained a TensorFlow GraphDef proto:
|
||||||
|
|
||||||
|
Message graph_def;
|
||||||
|
graph_def.ParseFromBytes(bytes, bytes_size);
|
||||||
|
|
||||||
|
You can then access the different fields of the GraphDef using the field
|
||||||
|
numbers assigned in the .proto file:
|
||||||
|
|
||||||
|
std::vector<picoproto::Message*> nodes = graph_def.GetMessageArray(1);
|
||||||
|
|
||||||
|
One big difference between this minimal approach and normal protobufs is that
|
||||||
|
the calling code has to already know the field number and type of any members
|
||||||
|
it's trying to access. Here I know that the `node` field is number 1, and that
|
||||||
|
it should contain a repeated list of NodeDefs. Since they are not primitive
|
||||||
|
types like numbers or strings, they are accessed as an array of Messages.
|
||||||
|
|
||||||
|
Here are the design goals of this module:
|
||||||
|
- Keep the code size tiny (single-digit kilobytes on most platforms).
|
||||||
|
- Minimize memory usage (for example allow in-place references to byte data).
|
||||||
|
- Provide a simple, readable implementation that can be ported easily.
|
||||||
|
- Deserialize all saved protobuf files into a usable representation.
|
||||||
|
- No dependencies other than the standard C++ library.
|
||||||
|
- No build-time support (e.g. protoc) required.
|
||||||
|
|
||||||
|
Here's what it's explicitly not offering:
|
||||||
|
- Providing a readable and transparent way of accessing serialized data.
|
||||||
|
- Saving out data to protobuf format.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDE_PICOPROTO_H
|
||||||
|
#define INCLUDE_PICOPROTO_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// To keep dependencies minimal, some bare-bones macros to make logging easier.
|
||||||
|
#define PP_LOG(X) PP_LOG_##X
|
||||||
|
#define PP_LOG_INFO std::cerr << __FILE__ << ":" << __LINE__ << " - INFO: "
|
||||||
|
#define PP_LOG_WARN std::cerr << __FILE__ << ":" << __LINE__ << " - WARN: "
|
||||||
|
#define PP_LOG_ERROR std::cerr << __FILE__ << ":" << __LINE__ << " - ERROR: "
|
||||||
|
#define PP_CHECK(X) \
|
||||||
|
if (!(X)) \
|
||||||
|
PP_LOG(ERROR) << "PP_CHECK(" << #X << ") failed. "
|
||||||
|
|
||||||
|
namespace picoproto
|
||||||
|
{
|
||||||
|
|
||||||
|
// These roughly correspond to the wire types used to save data in protobuf
|
||||||
|
// files. The best reference to understand the full format is:
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/encoding
|
||||||
|
// Because we don't know the bit-depth of VarInts, they're always stored as
|
||||||
|
// uint64 values, which is why there's no specific type for them.
|
||||||
|
enum FieldType
|
||||||
|
{
|
||||||
|
FIELD_UNSET,
|
||||||
|
FIELD_UINT32,
|
||||||
|
FIELD_UINT64,
|
||||||
|
FIELD_BYTES,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Gives a readable name for the field type for logging purposes.
|
||||||
|
std::string FieldTypeDebugString(enum FieldType type);
|
||||||
|
|
||||||
|
// Forward declare the main message class, since fields can contain them.
|
||||||
|
class Message;
|
||||||
|
|
||||||
|
// Fields are the building blocks of messages. They contain the values for each
|
||||||
|
// data member, and handle all the allocation and deallocation of storage.
|
||||||
|
// It's unlikely you'll want to access this class directly, since you'll
|
||||||
|
// normally want to use Message below to pull typed values.
|
||||||
|
class Field
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// You need to specify the type of a Field on creation, so that the right
|
||||||
|
// storage can be set up for the values. You also need to indicate whether the
|
||||||
|
// underlying memory will be around for the lifetime of the message (in which
|
||||||
|
// case no copies are needed) or whether the class should make copies and take
|
||||||
|
// ownership in case the data goes away.
|
||||||
|
Field(FieldType type, bool owns_data);
|
||||||
|
Field(const Field &other);
|
||||||
|
~Field();
|
||||||
|
|
||||||
|
enum FieldType type;
|
||||||
|
// I know, this isn't very OOP, but the simplicity of keeping track of a type
|
||||||
|
// and deciding how to initialize and access the data based on that persuaded
|
||||||
|
// me this was the best approach. The `value` member contains whatever data
|
||||||
|
// the field should be holding.
|
||||||
|
union
|
||||||
|
{
|
||||||
|
std::vector<uint32_t> *v_uint32;
|
||||||
|
std::vector<uint64_t> *v_uint64;
|
||||||
|
std::vector<std::pair<uint8_t *, size_t>> *v_bytes;
|
||||||
|
} value;
|
||||||
|
// One of the drawbacks of not requiring .proto files ahead of time is that I
|
||||||
|
// don't know if a length-delimited field contains raw bytes, strings, or
|
||||||
|
// sub-messages. The only time we know that a field should be interpreted as a
|
||||||
|
// message is when client code requests it in that form. Because parsing can
|
||||||
|
// be costly, here we cache the results of any such calls for subsequent
|
||||||
|
// accesses.
|
||||||
|
std::vector<Message *> *cached_messages;
|
||||||
|
// If this is set, then the object will allocate its own storage for
|
||||||
|
// length-delimited values, and copy from the input stream. If you know the
|
||||||
|
// underlying data will be around for the lifetime of the message, you can
|
||||||
|
// save memory and copies by leaving this as false.
|
||||||
|
bool owns_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The main interface for loading and accessing serialized protobuf data.
|
||||||
|
class Message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// If you're not sure about the lifetime of any binary data you're reading
|
||||||
|
// from, just call this default constructor.
|
||||||
|
Message();
|
||||||
|
// In the case when you're sure the lifetime of the byte stream you'll be
|
||||||
|
// decoding is longer than the lifetime of the message, you can set
|
||||||
|
// copy_arrays to false. This is especially useful if you have a memory
|
||||||
|
// mapped file to read from containing large binary blobs, since you'll skip
|
||||||
|
// a lot of copying and extra allocation.
|
||||||
|
Message(bool copy_arrays);
|
||||||
|
Message(const Message &other);
|
||||||
|
~Message();
|
||||||
|
|
||||||
|
// Populates fields with all of the data from this stream of bytes.
|
||||||
|
// You can call this repeatedly with new messages, and the results will be
|
||||||
|
// merged together.
|
||||||
|
bool ParseFromBytes(uint8_t *binary, size_t binary_size);
|
||||||
|
|
||||||
|
// These are the accessor functions if you're expecting exactly one value in a
|
||||||
|
// field. As discussed above, the burden is on the client code to know the
|
||||||
|
// field number and type of each member it's trying to access, and so pick the
|
||||||
|
// correct accessor function.
|
||||||
|
// If the field isn't present, this will raise an error, so if it's optional
|
||||||
|
// you should use the array accessors below.
|
||||||
|
int32_t GetInt32(int32_t number);
|
||||||
|
int64_t GetInt64(int32_t number);
|
||||||
|
uint32_t GetUInt32(int32_t number);
|
||||||
|
uint64_t GetUInt64(int32_t number);
|
||||||
|
int64_t GetInt(int32_t number);
|
||||||
|
bool GetBool(int32_t number);
|
||||||
|
float GetFloat(int32_t number);
|
||||||
|
double GetDouble(int32_t number);
|
||||||
|
std::pair<uint8_t *, size_t> GetBytes(int32_t number);
|
||||||
|
std::string GetString(int32_t number);
|
||||||
|
Message *GetMessage(int32_t number);
|
||||||
|
|
||||||
|
// If you're not sure if a value will be present, or if it is repeated, you
|
||||||
|
// should call these array functions. If no such field has been seen, then the
|
||||||
|
// result will be an empty vector, otherwise you'll get back one or more
|
||||||
|
// entries.
|
||||||
|
std::vector<int32_t> GetInt32Array(int32_t number);
|
||||||
|
std::vector<int64_t> GetInt64Array(int32_t number);
|
||||||
|
std::vector<uint32_t> GetUInt32Array(int32_t number);
|
||||||
|
std::vector<uint64_t> GetUInt64Array(int32_t number);
|
||||||
|
std::vector<bool> GetBoolArray(int32_t number);
|
||||||
|
std::vector<float> GetFloatArray(int32_t number);
|
||||||
|
std::vector<double> GetDoubleArray(int32_t number);
|
||||||
|
std::vector<std::pair<uint8_t *, size_t>> GetByteArray(int32_t number);
|
||||||
|
std::vector<std::string> GetStringArray(int32_t number);
|
||||||
|
std::vector<Message *> GetMessageArray(int32_t number);
|
||||||
|
|
||||||
|
// It's unlikely you'll want to access fields directly, but here's an escape
|
||||||
|
// hatch in case you do have to manipulate them more directly.
|
||||||
|
Field *GetField(int32_t number);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Inserts a new field, updating all the internal data structures.
|
||||||
|
Field *AddField(int32_t number, enum FieldType type);
|
||||||
|
|
||||||
|
Field *GetFieldAndCheckType(int32_t number, enum FieldType type);
|
||||||
|
|
||||||
|
// Maps from a field number to an index in the `fields` vector.
|
||||||
|
std::map<int32_t, size_t> field_map;
|
||||||
|
// The core list of fields that have been parsed.
|
||||||
|
std::vector<Field> fields;
|
||||||
|
bool copy_arrays;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace picoproto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PICOPROTO_H
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "qv2ray/utils/HTTPRequestHelper.hpp"
|
#include "qv2ray/v2/utils/HTTPRequestHelper.hpp"
|
||||||
|
|
||||||
#include "db/Database.hpp"
|
#include "db/Database.hpp"
|
||||||
#include "db/ProfileFilter.hpp"
|
#include "db/ProfileFilter.hpp"
|
||||||
|
|||||||
@@ -192,7 +192,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy profile share links</source>
|
<source>Copy profile share links</source>
|
||||||
<translation>复制所有配置的分享链接</translation>
|
<translation>复制分组内配置的分享链接</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copied</source>
|
<source>Copied</source>
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy profile share links (Nekoray)</source>
|
<source>Copy profile share links (Nekoray)</source>
|
||||||
<translation>复制所有配置的分享链接 (Nekoray)</translation>
|
<translation>复制分组内配置的分享链接 (Nekoray)</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@@ -293,7 +293,7 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>DialogHotkey</name>
|
<name>DialogHotkey</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Hot key</source>
|
<source>Hotkey</source>
|
||||||
<translation>热键</translation>
|
<translation>热键</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@@ -745,8 +745,8 @@
|
|||||||
<translation>停止</translation>
|
<translation>停止</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Routes</source>
|
<source>Routing VPN Settings</source>
|
||||||
<translation>路由</translation>
|
<translation>路由 VPN 设置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Add profile from clipboard</source>
|
<source>Add profile from clipboard</source>
|
||||||
@@ -924,7 +924,7 @@ End: %2</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remove Unavailable</source>
|
<source>Remove Unavailable</source>
|
||||||
<translation>删除不可用</translation>
|
<translation>删除不可用的配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
@@ -959,8 +959,8 @@ End: %2</source>
|
|||||||
<translation>删除 [ Delete ]</translation>
|
<translation>删除 [ Delete ]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hot key</source>
|
<source>Hotkey Settings</source>
|
||||||
<translation>热键</translation>
|
<translation>热键设置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>QR Code and link</source>
|
<source>QR Code and link</source>
|
||||||
@@ -987,12 +987,12 @@ End: %2</source>
|
|||||||
<translation>加载路由规则并应用: %1</translation>
|
<translation>加载路由规则并应用: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy links of selected</source>
|
<source>Copy links of selected [ Ctrl+C ]</source>
|
||||||
<translation>复制选中的分享链接</translation>
|
<translation>批量复制选中项目的分享链接 [ Ctrl+C ]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copied %1 item(s)</source>
|
<source>Copied %1 item(s)</source>
|
||||||
<translation>复制了%1 个项目</translation>
|
<translation>复制了 %1 个项目</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>New profile from clipboard</source>
|
<source>New profile from clipboard</source>
|
||||||
@@ -1012,7 +1012,7 @@ End: %2</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Delete Repeat</source>
|
<source>Delete Repeat</source>
|
||||||
<translation>删除重复</translation>
|
<translation>删除重复的配置</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select All</source>
|
<source>Select All</source>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "dialog_basic_settings.h"
|
#include "dialog_basic_settings.h"
|
||||||
#include "ui_dialog_basic_settings.h"
|
#include "ui_dialog_basic_settings.h"
|
||||||
|
|
||||||
#include "qv2ray/ui/widgets/editors/w_JsonEditor.hpp"
|
#include "qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
|
||||||
#include "ui/ThemeManager.hpp"
|
#include "ui/ThemeManager.hpp"
|
||||||
#include "main/GuiUtils.hpp"
|
#include "main/GuiUtils.hpp"
|
||||||
#include "main/NekoRay.hpp"
|
#include "main/NekoRay.hpp"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Hot key</string>
|
<string>Hotkey</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#include "dialog_manage_routes.h"
|
#include "dialog_manage_routes.h"
|
||||||
#include "ui_dialog_manage_routes.h"
|
#include "ui_dialog_manage_routes.h"
|
||||||
|
|
||||||
#include "qv2ray/ui/widgets/editors/w_JsonEditor.hpp"
|
#include "qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
|
||||||
|
#include "qv2ray/v3/components/GeositeReader/GeositeReader.hpp"
|
||||||
#include "main/GuiUtils.hpp"
|
#include "main/GuiUtils.hpp"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -59,14 +60,23 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) :
|
|||||||
builtInSchemesMenu->addActions(this->getBuiltInSchemes());
|
builtInSchemesMenu->addActions(this->getBuiltInSchemes());
|
||||||
ui->preset->setMenu(builtInSchemesMenu);
|
ui->preset->setMenu(builtInSchemesMenu);
|
||||||
|
|
||||||
|
QString geoipFn = QApplication::applicationDirPath() + "/geoip.dat";
|
||||||
|
QString geositeFn = QApplication::applicationDirPath() + +"/geosite.dat";
|
||||||
|
if (!NekoRay::dataStore->v2ray_asset_dir.isEmpty()) {
|
||||||
|
geoipFn = NekoRay::dataStore->v2ray_asset_dir + "/geoip.dat";
|
||||||
|
geositeFn = NekoRay::dataStore->v2ray_asset_dir + "/geosite.dat";
|
||||||
|
}
|
||||||
//
|
//
|
||||||
directDomainTxt = new AutoCompleteTextEdit("geosite", {}, this);
|
const auto sourceStringsDomain = Qv2ray::components::GeositeReader::ReadGeoSiteFromFile(geoipFn);
|
||||||
proxyDomainTxt = new AutoCompleteTextEdit("geosite", {}, this);
|
directDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this);
|
||||||
blockDomainTxt = new AutoCompleteTextEdit("geosite", {}, this);
|
proxyDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this);
|
||||||
|
blockDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this);
|
||||||
//
|
//
|
||||||
directIPTxt = new AutoCompleteTextEdit("geoip", {}, this);
|
const auto sourceStringsIP = Qv2ray::components::GeositeReader::ReadGeoSiteFromFile(geositeFn);
|
||||||
proxyIPTxt = new AutoCompleteTextEdit("geoip", {}, this);
|
qDebug() << sourceStringsIP;
|
||||||
blockIPTxt = new AutoCompleteTextEdit("geoip", {}, this);
|
directIPTxt = new AutoCompleteTextEdit("geoip", sourceStringsIP, this);
|
||||||
|
proxyIPTxt = new AutoCompleteTextEdit("geoip", sourceStringsIP, this);
|
||||||
|
blockIPTxt = new AutoCompleteTextEdit("geoip", sourceStringsIP, this);
|
||||||
//
|
//
|
||||||
ui->directTxtLayout->addWidget(directDomainTxt, 0, 0);
|
ui->directTxtLayout->addWidget(directDomainTxt, 0, 0);
|
||||||
ui->proxyTxtLayout->addWidget(proxyDomainTxt, 0, 0);
|
ui->proxyTxtLayout->addWidget(proxyDomainTxt, 0, 0);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
|
||||||
#include "qv2ray/ui/QvAutoCompleteTextEdit.hpp"
|
#include "qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp"
|
||||||
#include "main/NekoRay.hpp"
|
#include "main/NekoRay.hpp"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|||||||
@@ -119,7 +119,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="copy_links">
|
<widget class="QPushButton" name="copy_links">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
#include "ui/edit/edit_custom.h"
|
#include "ui/edit/edit_custom.h"
|
||||||
|
|
||||||
#include "fmt/includes.h"
|
#include "fmt/includes.h"
|
||||||
#include "qv2ray/ui/widgets/editors/w_JsonEditor.hpp"
|
|
||||||
|
#include "qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
|
||||||
#include "main/GuiUtils.hpp"
|
#include "main/GuiUtils.hpp"
|
||||||
|
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "edit_custom.h"
|
#include "edit_custom.h"
|
||||||
#include "ui_edit_custom.h"
|
#include "ui_edit_custom.h"
|
||||||
|
|
||||||
#include "qv2ray/ui/widgets/editors/w_JsonEditor.hpp"
|
#include "qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
|
||||||
#include "fmt/CustomBean.hpp"
|
#include "fmt/CustomBean.hpp"
|
||||||
|
|
||||||
EditCustom::EditCustom(QWidget *parent) :
|
EditCustom::EditCustom(QWidget *parent) :
|
||||||
|
|||||||
@@ -16,12 +16,12 @@
|
|||||||
|
|
||||||
#include "3rdparty/qrcodegen.hpp"
|
#include "3rdparty/qrcodegen.hpp"
|
||||||
#include "3rdparty/VT100Parser.hpp"
|
#include "3rdparty/VT100Parser.hpp"
|
||||||
#include "qv2ray/ui/LogHighlighter.hpp"
|
#include "qv2ray/v2/ui/LogHighlighter.hpp"
|
||||||
|
|
||||||
#ifndef NKR_NO_EXTERNAL
|
#ifndef NKR_NO_EXTERNAL
|
||||||
|
|
||||||
#include "3rdparty/ZxingQtReader.hpp"
|
#include "3rdparty/ZxingQtReader.hpp"
|
||||||
#include "qv2ray/components/proxy/QvProxyConfigurator.hpp"
|
#include "qv2ray/v2/components/proxy/QvProxyConfigurator.hpp"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -254,6 +254,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
//
|
//
|
||||||
ui->menu_program_preference->addActions(ui->menu_preferences->actions());
|
ui->menu_program_preference->addActions(ui->menu_preferences->actions());
|
||||||
connect(ui->menu_add_from_clipboard2, &QAction::triggered, ui->menu_add_from_clipboard, &QAction::trigger);
|
connect(ui->menu_add_from_clipboard2, &QAction::triggered, ui->menu_add_from_clipboard, &QAction::trigger);
|
||||||
|
connect(shortcut_ctrl_c, &QShortcut::activated, ui->menu_copy_links, &QAction::trigger);
|
||||||
connect(shortcut_ctrl_v, &QShortcut::activated, ui->menu_add_from_clipboard, &QAction::trigger);
|
connect(shortcut_ctrl_v, &QShortcut::activated, ui->menu_add_from_clipboard, &QAction::trigger);
|
||||||
//
|
//
|
||||||
connect(ui->menu_program, &QMenu::aboutToShow, this, [=]() {
|
connect(ui->menu_program, &QMenu::aboutToShow, this, [=]() {
|
||||||
@@ -938,6 +939,7 @@ void MainWindow::on_menu_copy_links_triggered() {
|
|||||||
for (const auto &ent: ents) {
|
for (const auto &ent: ents) {
|
||||||
links += ent->bean->ToShareLink();
|
links += ent->bean->ToShareLink();
|
||||||
}
|
}
|
||||||
|
if (links.length() == 0) return;
|
||||||
QApplication::clipboard()->setText(links.join("\n"));
|
QApplication::clipboard()->setText(links.join("\n"));
|
||||||
MessageBoxInfo("NekoRay", tr("Copied %1 item(s)").arg(links.length()));
|
MessageBoxInfo("NekoRay", tr("Copied %1 item(s)").arg(links.length()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ private:
|
|||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
QSystemTrayIcon *tray;
|
QSystemTrayIcon *tray;
|
||||||
QShortcut *shortcut_ctrl_f = new QShortcut(QKeySequence("Ctrl+F"), this);
|
QShortcut *shortcut_ctrl_f = new QShortcut(QKeySequence("Ctrl+F"), this);
|
||||||
|
QShortcut *shortcut_ctrl_c = new QShortcut(QKeySequence("Ctrl+C"), this);
|
||||||
QShortcut *shortcut_ctrl_v = new QShortcut(QKeySequence("Ctrl+V"), this);
|
QShortcut *shortcut_ctrl_v = new QShortcut(QKeySequence("Ctrl+V"), this);
|
||||||
QShortcut *shortcut_esc = new QShortcut(QKeySequence("Esc"), this);
|
QShortcut *shortcut_esc = new QShortcut(QKeySequence("Esc"), this);
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -498,8 +498,8 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Preferences</string>
|
<string>Preferences</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="menu_basic_settings"/>
|
|
||||||
<addaction name="menu_manage_groups"/>
|
<addaction name="menu_manage_groups"/>
|
||||||
|
<addaction name="menu_basic_settings"/>
|
||||||
<addaction name="menu_routing_settings"/>
|
<addaction name="menu_routing_settings"/>
|
||||||
<addaction name="menu_hotkey_settings"/>
|
<addaction name="menu_hotkey_settings"/>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -582,7 +582,7 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="menu_routing_settings">
|
<action name="menu_routing_settings">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Routes</string>
|
<string>Routing VPN Settings</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="menu_add_from_clipboard">
|
<action name="menu_add_from_clipboard">
|
||||||
@@ -715,7 +715,7 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="menu_hotkey_settings">
|
<action name="menu_hotkey_settings">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Hot key</string>
|
<string>Hotkey Settings</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="menu_select_all">
|
<action name="menu_select_all">
|
||||||
@@ -746,7 +746,7 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="menu_copy_links">
|
<action name="menu_copy_links">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Copy links of selected</string>
|
<string>Copy links of selected [ Ctrl+C ]</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="menu_spmode_vpn">
|
<action name="menu_spmode_vpn">
|
||||||
|
|||||||
Reference in New Issue
Block a user