mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-17 12:34:37 +03:00
improve ui
This commit is contained in:
@@ -89,15 +89,17 @@ set(PROJECT_SOURCES
|
||||
3rdparty/qrcodegen.cpp
|
||||
3rdparty/QtExtKeySequenceEdit.cpp
|
||||
|
||||
qv2ray/ui/LogHighlighter.cpp
|
||||
qv2ray/ui/QvAutoCompleteTextEdit.cpp
|
||||
qv2ray/utils/HTTPRequestHelper.cpp
|
||||
qv2ray/components/proxy/QvProxyConfigurator.cpp
|
||||
qv2ray/ui/widgets/common/QJsonModel.cpp
|
||||
qv2ray/v2/ui/LogHighlighter.cpp
|
||||
qv2ray/v2/ui/QvAutoCompleteTextEdit.cpp
|
||||
qv2ray/v2/utils/HTTPRequestHelper.cpp
|
||||
qv2ray/v2/components/proxy/QvProxyConfigurator.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/ui/widgets/editors/w_JsonEditor.hpp
|
||||
qv2ray/ui/widgets/editors/w_JsonEditor.ui
|
||||
qv2ray/v3/components/GeositeReader/GeositeReader.cpp
|
||||
qv2ray/v3/components/GeositeReader/picoproto.cpp
|
||||
|
||||
rpc/gRPC.cpp
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "qv2ray/wrapper.hpp"
|
||||
#include "qv2ray/ui/widgets/common/QJsonModel.hpp"
|
||||
#include "qv2ray/v2/ui/widgets/common/QJsonModel.hpp"
|
||||
#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/ProfileFilter.hpp"
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy profile share links</source>
|
||||
<translation>复制所有配置的分享链接</translation>
|
||||
<translation>复制分组内配置的分享链接</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copied</source>
|
||||
@@ -200,7 +200,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy profile share links (Nekoray)</source>
|
||||
<translation>复制所有配置的分享链接 (Nekoray)</translation>
|
||||
<translation>复制分组内配置的分享链接 (Nekoray)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -293,7 +293,7 @@
|
||||
<context>
|
||||
<name>DialogHotkey</name>
|
||||
<message>
|
||||
<source>Hot key</source>
|
||||
<source>Hotkey</source>
|
||||
<translation>热键</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -745,8 +745,8 @@
|
||||
<translation>停止</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Routes</source>
|
||||
<translation>路由</translation>
|
||||
<source>Routing VPN Settings</source>
|
||||
<translation>路由 VPN 设置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add profile from clipboard</source>
|
||||
@@ -924,7 +924,7 @@ End: %2</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove Unavailable</source>
|
||||
<translation>删除不可用</translation>
|
||||
<translation>删除不可用的配置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
@@ -959,8 +959,8 @@ End: %2</source>
|
||||
<translation>删除 [ Delete ]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hot key</source>
|
||||
<translation>热键</translation>
|
||||
<source>Hotkey Settings</source>
|
||||
<translation>热键设置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>QR Code and link</source>
|
||||
@@ -987,12 +987,12 @@ End: %2</source>
|
||||
<translation>加载路由规则并应用: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy links of selected</source>
|
||||
<translation>复制选中的分享链接</translation>
|
||||
<source>Copy links of selected [ Ctrl+C ]</source>
|
||||
<translation>批量复制选中项目的分享链接 [ Ctrl+C ]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copied %1 item(s)</source>
|
||||
<translation>复制了%1 个项目</translation>
|
||||
<translation>复制了 %1 个项目</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New profile from clipboard</source>
|
||||
@@ -1012,7 +1012,7 @@ End: %2</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Repeat</source>
|
||||
<translation>删除重复</translation>
|
||||
<translation>删除重复的配置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select All</source>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "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 "main/GuiUtils.hpp"
|
||||
#include "main/NekoRay.hpp"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Hot key</string>
|
||||
<string>Hotkey</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "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 <QFile>
|
||||
@@ -59,14 +60,23 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) :
|
||||
builtInSchemesMenu->addActions(this->getBuiltInSchemes());
|
||||
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);
|
||||
proxyDomainTxt = new AutoCompleteTextEdit("geosite", {}, this);
|
||||
blockDomainTxt = new AutoCompleteTextEdit("geosite", {}, this);
|
||||
const auto sourceStringsDomain = Qv2ray::components::GeositeReader::ReadGeoSiteFromFile(geoipFn);
|
||||
directDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this);
|
||||
proxyDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this);
|
||||
blockDomainTxt = new AutoCompleteTextEdit("geosite", sourceStringsDomain, this);
|
||||
//
|
||||
directIPTxt = new AutoCompleteTextEdit("geoip", {}, this);
|
||||
proxyIPTxt = new AutoCompleteTextEdit("geoip", {}, this);
|
||||
blockIPTxt = new AutoCompleteTextEdit("geoip", {}, this);
|
||||
const auto sourceStringsIP = Qv2ray::components::GeositeReader::ReadGeoSiteFromFile(geositeFn);
|
||||
qDebug() << sourceStringsIP;
|
||||
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->proxyTxtLayout->addWidget(proxyDomainTxt, 0, 0);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <QDialog>
|
||||
#include <QMenu>
|
||||
|
||||
#include "qv2ray/ui/QvAutoCompleteTextEdit.hpp"
|
||||
#include "qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp"
|
||||
#include "main/NekoRay.hpp"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="copy_links">
|
||||
<property name="text">
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
#include "ui/edit/edit_custom.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 <QInputDialog>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "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"
|
||||
|
||||
EditCustom::EditCustom(QWidget *parent) :
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
#include "3rdparty/qrcodegen.hpp"
|
||||
#include "3rdparty/VT100Parser.hpp"
|
||||
#include "qv2ray/ui/LogHighlighter.hpp"
|
||||
#include "qv2ray/v2/ui/LogHighlighter.hpp"
|
||||
|
||||
#ifndef NKR_NO_EXTERNAL
|
||||
|
||||
#include "3rdparty/ZxingQtReader.hpp"
|
||||
#include "qv2ray/components/proxy/QvProxyConfigurator.hpp"
|
||||
#include "qv2ray/v2/components/proxy/QvProxyConfigurator.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -254,6 +254,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
//
|
||||
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(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(ui->menu_program, &QMenu::aboutToShow, this, [=]() {
|
||||
@@ -938,6 +939,7 @@ void MainWindow::on_menu_copy_links_triggered() {
|
||||
for (const auto &ent: ents) {
|
||||
links += ent->bean->ToShareLink();
|
||||
}
|
||||
if (links.length() == 0) return;
|
||||
QApplication::clipboard()->setText(links.join("\n"));
|
||||
MessageBoxInfo("NekoRay", tr("Copied %1 item(s)").arg(links.length()));
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ private:
|
||||
Ui::MainWindow *ui;
|
||||
QSystemTrayIcon *tray;
|
||||
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_esc = new QShortcut(QKeySequence("Esc"), this);
|
||||
//
|
||||
|
||||
@@ -498,8 +498,8 @@
|
||||
<property name="title">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<addaction name="menu_basic_settings"/>
|
||||
<addaction name="menu_manage_groups"/>
|
||||
<addaction name="menu_basic_settings"/>
|
||||
<addaction name="menu_routing_settings"/>
|
||||
<addaction name="menu_hotkey_settings"/>
|
||||
</widget>
|
||||
@@ -582,7 +582,7 @@
|
||||
</action>
|
||||
<action name="menu_routing_settings">
|
||||
<property name="text">
|
||||
<string>Routes</string>
|
||||
<string>Routing VPN Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_add_from_clipboard">
|
||||
@@ -715,7 +715,7 @@
|
||||
</action>
|
||||
<action name="menu_hotkey_settings">
|
||||
<property name="text">
|
||||
<string>Hot key</string>
|
||||
<string>Hotkey Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_select_all">
|
||||
@@ -746,7 +746,7 @@
|
||||
</action>
|
||||
<action name="menu_copy_links">
|
||||
<property name="text">
|
||||
<string>Copy links of selected</string>
|
||||
<string>Copy links of selected [ Ctrl+C ]</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menu_spmode_vpn">
|
||||
|
||||
Reference in New Issue
Block a user