Compare commits
	
		
			16 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e13314f5dc | |||
| b50c3e738a | |||
| 54ad6015b7 | |||
| 40e84ac230 | |||
| 767eaa0a47 | |||
| bb594e768c | |||
| 516bd99c4d | |||
| 571bfcf4b6 | |||
| 26f7a11d80 | |||
| 21588021d3 | |||
| 7441e0c15b | |||
| 142f3f6bf4 | |||
| 19ddb1c956 | |||
| 97467eae4d | |||
| 17c37b6568 | |||
| 57237ef2e8 | 
							
								
								
									
										1
									
								
								.hgtags
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.hgtags
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					74cacbbe8f6cfb20a63902fb76e48c02d5cf2963 release-1.0.0
 | 
				
			||||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export PATH := /usr/lib/mxe/usr/bin:$(PATH)
 | 
					export PATH := /usr/lib/mxe/usr/bin:$(PATH)
 | 
				
			||||||
GOFLAGS := -ldflags='-s -w' -gcflags='-trimpath=$(CURDIR)' -asmflags='-trimpath=$(CURDIR)'
 | 
					GOFLAGS := -ldflags='-s -w' -gcflags='-trimpath=$(CURDIR)' -asmflags='-trimpath=$(CURDIR)'
 | 
				
			||||||
VERSION := 1.0.0
 | 
					VERSION := 1.0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: all libs dist clean
 | 
					.PHONY: all libs dist clean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,9 @@ Written in C++ (Qt), Golang (CGo)
 | 
				
			|||||||
=FEATURES=
 | 
					=FEATURES=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Open existing database or create new database
 | 
					- Open existing database or create new database
 | 
				
			||||||
- Supports nested buckets
 | 
					- Option to open database as readonly for concurrent use
 | 
				
			||||||
- Create and edit keys
 | 
					- Create, list, edit and delete keys and buckets (including nested buckets)
 | 
				
			||||||
 | 
					- Safe for use with arbitrary binary key/bucket names (new ones created in UTF-8)
 | 
				
			||||||
- View database and bucket statistics
 | 
					- View database and bucket statistics
 | 
				
			||||||
- 100% Bolt compatibility via the real codebase
 | 
					- 100% Bolt compatibility via the real codebase
 | 
				
			||||||
- Tested working on both Windows and Linux
 | 
					- Tested working on both Windows and Linux
 | 
				
			||||||
@@ -27,5 +28,11 @@ The Windows binary is released under LGPL-3+ owing to the static copy of Qt.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
=CHANGELOG=
 | 
					=CHANGELOG=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2017-06-19 1.0.1
 | 
				
			||||||
 | 
					- Feature: Option to open database as read-only
 | 
				
			||||||
 | 
					- Fix an issue with support for bucket names and keys not surviving UTF-8 roundtrips (now binary-clean)
 | 
				
			||||||
 | 
					- Fix an issue with crashing when deleting a bucket other than the selected one
 | 
				
			||||||
 | 
					- Fix a cosmetic issue with application icon on Windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2017-05-21 1.0.0
 | 
					2017-05-21 1.0.0
 | 
				
			||||||
- Initial public release
 | 
					- Initial public release
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- Extra option to open read-only (allows concurrent access)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- Reduce unnecessary memory copies between QString/QByteArray
 | 
					- Reduce unnecessary memory copies between QString/QByteArray
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Convert from item-based to model-based Qt widgets - needs deep integration with Bolt cursors...
 | 
					- Convert from item-based to model-based Qt widgets - needs deep integration with Bolt cursors...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Remove dependence on UTF-8-encoded keys
 | 
					- Rename buckets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Delete multiple buckets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								_dist/image0.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								_dist/image0.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 50 KiB  | 
@@ -1,7 +1,7 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						//"fmt"
 | 
				
			||||||
	"math/rand"
 | 
						"math/rand"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -9,7 +9,10 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func random_name() string {
 | 
					func random_name() string {
 | 
				
			||||||
	return fmt.Sprintf("%08x-%08x-%08x", rand.Int63(), rand.Int63(), rand.Int63())
 | 
						ret := make([]byte, 12)
 | 
				
			||||||
 | 
						rand.Read(ret)
 | 
				
			||||||
 | 
						return string(ret)
 | 
				
			||||||
 | 
						//return fmt.Sprintf("%08x-%08x-%08x", rand.Int63(), rand.Int63(), rand.Int63())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func fill_bucket(tx *bolt.Tx, bucket *bolt.Bucket) error {
 | 
					func fill_bucket(tx *bolt.Tx, bucket *bolt.Bucket) error {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.go
									
									
									
									
									
								
							@@ -26,9 +26,11 @@ func GetMagic() int64 {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//export Bolt_Open
 | 
					//export Bolt_Open
 | 
				
			||||||
func Bolt_Open(path string) (ObjectReference, *C.char, int) {
 | 
					func Bolt_Open(readOnly bool, path string) (ObjectReference, *C.char, int) {
 | 
				
			||||||
	opts := *bolt.DefaultOptions
 | 
						opts := *bolt.DefaultOptions
 | 
				
			||||||
	opts.Timeout = 10 * time.Second
 | 
						opts.Timeout = 10 * time.Second
 | 
				
			||||||
 | 
						opts.ReadOnly = readOnly
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ptrDB, err := bolt.Open(path, os.FileMode(0644), &opts)
 | 
						ptrDB, err := bolt.Open(path, os.FileMode(0644), &opts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		errMsg := err.Error()
 | 
							errMsg := err.Error()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,12 +7,12 @@ BoltDB::BoltDB()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BoltDB* BoltDB::createFrom(QString filePath, QString &errorOut)
 | 
					BoltDB* BoltDB::createFrom(QString filePath, bool readOnly, QString &errorOut)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    QByteArray filePathBytes(filePath.toUtf8());
 | 
					    QByteArray filePathBytes(filePath.toUtf8());
 | 
				
			||||||
    GoString filePathGS = Interop::toGoString_WeakRef(&filePathBytes);
 | 
					    GoString filePathGS = Interop::toGoString_WeakRef(&filePathBytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto open_ret = ::Bolt_Open(filePathGS);
 | 
					    auto open_ret = ::Bolt_Open(readOnly, filePathGS);
 | 
				
			||||||
    if (open_ret.r2 != 0) {
 | 
					    if (open_ret.r2 != 0) {
 | 
				
			||||||
        errorOut = QString::fromUtf8(open_ret.r1, open_ret.r2);
 | 
					        errorOut = QString::fromUtf8(open_ret.r1, open_ret.r2);
 | 
				
			||||||
        free(open_ret.r1);
 | 
					        free(open_ret.r1);
 | 
				
			||||||
@@ -45,27 +45,27 @@ static bool handleTriple(int r0, char* r1, int64_t r2, QString& errorOut) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BoltDB::addBucket(QStringList bucketPath, QByteArray bucketName, QString& errorOut)
 | 
					bool BoltDB::addBucket(const QList<QByteArray>& bucketPath, QByteArray bucketName, QString& errorOut)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GoSliceManagedWrapper browse(&bucketPath);
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
    GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
 | 
					    GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
 | 
				
			||||||
    auto resp = ::Bolt_CreateBucket(this->gmsDbRef, browse.slice, bucketNameGS);
 | 
					    auto resp = ::Bolt_CreateBucket(this->gmsDbRef, browse.slice, bucketNameGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
					    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BoltDB::deleteBucket(QStringList bucketPath, QByteArray bucketName, QString& errorOut)
 | 
					bool BoltDB::deleteBucket(const QList<QByteArray>& bucketPath, QByteArray bucketName, QString& errorOut)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GoSliceManagedWrapper browse(&bucketPath);
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
    GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
 | 
					    GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
 | 
				
			||||||
    auto resp = ::Bolt_DeleteBucket(this->gmsDbRef, browse.slice, bucketNameGS);
 | 
					    auto resp = ::Bolt_DeleteBucket(this->gmsDbRef, browse.slice, bucketNameGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
					    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BoltDB::setItem(QStringList bucketPath, QByteArray keyName, QByteArray value, QString& errorOut)
 | 
					bool BoltDB::setItem(const QList<QByteArray>& bucketPath, QByteArray keyName, QByteArray value, QString& errorOut)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GoSliceManagedWrapper browse(&bucketPath);
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
    GoString keyNameGS = Interop::toGoString_WeakRef(&keyName);
 | 
					    GoString keyNameGS = Interop::toGoString_WeakRef(&keyName);
 | 
				
			||||||
    GoString valueGS = Interop::toGoString_WeakRef(&value);
 | 
					    GoString valueGS = Interop::toGoString_WeakRef(&value);
 | 
				
			||||||
    auto resp = ::Bolt_SetItem(this->gmsDbRef, browse.slice, keyNameGS, valueGS);
 | 
					    auto resp = ::Bolt_SetItem(this->gmsDbRef, browse.slice, keyNameGS, valueGS);
 | 
				
			||||||
@@ -73,9 +73,9 @@ bool BoltDB::setItem(QStringList bucketPath, QByteArray keyName, QByteArray valu
 | 
				
			|||||||
    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
					    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BoltDB::deleteItem(QStringList bucketPath, QByteArray keyName, QString& errorOut)
 | 
					bool BoltDB::deleteItem(const QList<QByteArray>& bucketPath, QByteArray keyName, QString& errorOut)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GoSliceManagedWrapper browse(&bucketPath);
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
    GoString keyNameGS = Interop::toGoString_WeakRef(&keyName);
 | 
					    GoString keyNameGS = Interop::toGoString_WeakRef(&keyName);
 | 
				
			||||||
    auto resp = ::Bolt_DeleteItem(this->gmsDbRef, browse.slice, keyNameGS);
 | 
					    auto resp = ::Bolt_DeleteItem(this->gmsDbRef, browse.slice, keyNameGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -89,20 +89,20 @@ bool BoltDB::listBucketsAtRoot(QString& errorOut, NameReciever cb)
 | 
				
			|||||||
    return pumpNext(listJob, errorOut, cb);
 | 
					    return pumpNext(listJob, errorOut, cb);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BoltDB::listBuckets(QStringList bucketPath, QString& errorOut, NameReciever cb)
 | 
					bool BoltDB::listBuckets(const QList<QByteArray>& bucketPath, QString& errorOut, NameReciever cb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (bucketPath.size() == 0) {
 | 
					    if (bucketPath.size() == 0) {
 | 
				
			||||||
        return listBucketsAtRoot(errorOut, cb);
 | 
					        return listBucketsAtRoot(errorOut, cb);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GoSliceManagedWrapper browse(&bucketPath);
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
    auto listJob = ::Bolt_ListBuckets(this->gmsDbRef, browse.slice);
 | 
					    auto listJob = ::Bolt_ListBuckets(this->gmsDbRef, browse.slice);
 | 
				
			||||||
    return pumpNext(listJob, errorOut, cb);
 | 
					    return pumpNext(listJob, errorOut, cb);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BoltDB::listKeys(QStringList bucketPath, QString& errorOut, std::function<void(QByteArray, int64_t)> cb)
 | 
					bool BoltDB::listKeys(const QList<QByteArray>& bucketPath, QString& errorOut, std::function<void(QByteArray, int64_t)> cb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GoSliceManagedWrapper browse(&bucketPath);
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
    auto listJob = ::Bolt_ListItems(this->gmsDbRef, browse.slice);
 | 
					    auto listJob = ::Bolt_ListItems(this->gmsDbRef, browse.slice);
 | 
				
			||||||
    return pumpNext(listJob, errorOut, [=](QByteArray b) {
 | 
					    return pumpNext(listJob, errorOut, [=](QByteArray b) {
 | 
				
			||||||
        // First 8 bytes are little-endian uint64 len
 | 
					        // First 8 bytes are little-endian uint64 len
 | 
				
			||||||
@@ -111,9 +111,9 @@ bool BoltDB::listKeys(QStringList bucketPath, QString& errorOut, std::function<v
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BoltDB::getData(QStringList bucketPath, QByteArray key, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError)
 | 
					bool BoltDB::getData(const QList<QByteArray>& bucketPath, QByteArray key, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GoSliceManagedWrapper browse(&bucketPath);
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
    GoString keyGS = Interop::toGoString_WeakRef(&key);
 | 
					    GoString keyGS = Interop::toGoString_WeakRef(&key);
 | 
				
			||||||
    auto resp = ::Bolt_GetItem(this->gmsDbRef, browse.slice, keyGS);
 | 
					    auto resp = ::Bolt_GetItem(this->gmsDbRef, browse.slice, keyGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -185,9 +185,9 @@ bool BoltDB::getStatsJSON(std::function<void(QByteArray)> onSuccess, std::functi
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BoltDB::getBucketStatsJSON(QStringList bucketPath, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError)
 | 
					bool BoltDB::getBucketStatsJSON(const QList<QByteArray>& bucketPath, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GoSliceManagedWrapper sliceWrapper(&bucketPath);
 | 
					    GoSliceManagedWrapper sliceWrapper(bucketPath);
 | 
				
			||||||
    auto statresp = Bolt_BucketStats(this->gmsDbRef, sliceWrapper.slice);
 | 
					    auto statresp = Bolt_BucketStats(this->gmsDbRef, sliceWrapper.slice);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (statresp.r0 == ERROR_AND_STOP_CALLING) {
 | 
					    if (statresp.r0 == ERROR_AND_STOP_CALLING) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,27 +14,27 @@ protected:
 | 
				
			|||||||
    GoInt64 gmsDbRef;
 | 
					    GoInt64 gmsDbRef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    static BoltDB* createFrom(QString filePath, QString &errorOut);
 | 
					    static BoltDB* createFrom(QString filePath, bool readOnly, QString &errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool listBucketsAtRoot(QString& errorOut, NameReciever cb);
 | 
					    bool listBucketsAtRoot(QString& errorOut, NameReciever cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool listBuckets(QStringList bucketPath, QString& errorOut, NameReciever cb);
 | 
					    bool listBuckets(const QList<QByteArray>& bucketPath, QString& errorOut, NameReciever cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool addBucket(QStringList bucketPath, QByteArray bucketName, QString& errorOut);
 | 
					    bool addBucket(const QList<QByteArray>& bucketPath, QByteArray bucketName, QString& errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool deleteBucket(QStringList bucketPath, QByteArray bucketName, QString& errorOut);
 | 
					    bool deleteBucket(const QList<QByteArray>& bucketPath, QByteArray bucketName, QString& errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool setItem(QStringList bucketPath, QByteArray keyName, QByteArray value, QString& errorOut);
 | 
					    bool setItem(const QList<QByteArray>& bucketPath, QByteArray keyName, QByteArray value, QString& errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool deleteItem(QStringList bucketPath, QByteArray keyName, QString& errorOut);
 | 
					    bool deleteItem(const QList<QByteArray>& bucketPath, QByteArray keyName, QString& errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool listKeys(QStringList bucketPath, QString& errorOut, std::function<void(QByteArray, int64_t)> cb);
 | 
					    bool listKeys(const QList<QByteArray>& bucketPath, QString& errorOut, std::function<void(QByteArray, int64_t)> cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool getData(QStringList bucketPath, QByteArray key, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError);
 | 
					    bool getData(const QList<QByteArray>& bucketPath, QByteArray key, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool getStatsJSON(std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError);
 | 
					    bool getStatsJSON(std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool getBucketStatsJSON(QStringList bucketPath, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError);
 | 
					    bool getBucketStatsJSON(const QList<QByteArray>& bucketPath, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ~BoltDB();
 | 
					    ~BoltDB();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,22 +17,22 @@ int64_t Interop::GetMagic() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GoSliceManagedWrapper::GoSliceManagedWrapper(QStringList *qsl) :
 | 
					GoSliceManagedWrapper::GoSliceManagedWrapper(const QList<QByteArray>& qsl) :
 | 
				
			||||||
    rawStrings(),
 | 
					    rawStrings(),
 | 
				
			||||||
    slice(),
 | 
					    slice(),
 | 
				
			||||||
    strings(nullptr)
 | 
					    strings(nullptr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    rawStrings.reserve(qsl->size());
 | 
					    rawStrings.reserve(qsl.size());
 | 
				
			||||||
    strings = new GoString[qsl->size()];
 | 
					    strings = new GoString[qsl.size()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (int i = 0; i < qsl->size(); ++i) {
 | 
					    for (int i = 0; i < qsl.size(); ++i) {
 | 
				
			||||||
        rawStrings.push_back( qsl->at(i).toUtf8() );
 | 
					        rawStrings.push_back( qsl.at(i) );
 | 
				
			||||||
        strings[i].p = rawStrings[i].data();
 | 
					        strings[i].p = rawStrings[i].data();
 | 
				
			||||||
        strings[i].n = rawStrings[i].size();
 | 
					        strings[i].n = rawStrings[i].size();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    slice.data = static_cast<void*>(strings);
 | 
					    slice.data = static_cast<void*>(strings);
 | 
				
			||||||
    slice.len  = qsl->size(); //  * sizeof(GoString);
 | 
					    slice.len  = qsl.size(); //  * sizeof(GoString);
 | 
				
			||||||
    slice.cap  = slice.len;
 | 
					    slice.cap  = slice.len;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ class GoSliceManagedWrapper {
 | 
				
			|||||||
    Q_DISABLE_COPY(GoSliceManagedWrapper)
 | 
					    Q_DISABLE_COPY(GoSliceManagedWrapper)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    GoSliceManagedWrapper(QStringList *qsl);
 | 
					    GoSliceManagedWrapper(const QList<QByteArray>& qsl);
 | 
				
			||||||
    ~GoSliceManagedWrapper();
 | 
					    ~GoSliceManagedWrapper();
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
    QList<QByteArray> rawStrings;
 | 
					    QList<QByteArray> rawStrings;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,8 @@ MainWindow::~MainWindow()
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const int BdbPointerRole = Qt::UserRole + 1;
 | 
					static const int BdbPointerRole = Qt::UserRole + 1;
 | 
				
			||||||
 | 
					static const int BinaryDataRole = Qt::UserRole + 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SET_BDB(top, bdb) top->setData(0, BdbPointerRole, QVariant::fromValue<void*>(static_cast<void*>(bdb)))
 | 
					#define SET_BDB(top, bdb) top->setData(0, BdbPointerRole, QVariant::fromValue<void*>(static_cast<void*>(bdb)))
 | 
				
			||||||
#define GET_BDB(top) static_cast<BoltDB*>( top->data(0, BdbPointerRole).value<void*>() )
 | 
					#define GET_BDB(top) static_cast<BoltDB*>( top->data(0, BdbPointerRole).value<void*>() )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,7 +44,7 @@ void MainWindow::on_actionNew_database_triggered()
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    QString file = QFileDialog::getSaveFileName(this, tr("Save new bolt database as..."));
 | 
					    QString file = QFileDialog::getSaveFileName(this, tr("Save new bolt database as..."));
 | 
				
			||||||
    if (file.length()) {
 | 
					    if (file.length()) {
 | 
				
			||||||
        openDatabase(file);
 | 
					        openDatabase(file, false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -50,15 +52,23 @@ void MainWindow::on_actionOpen_database_triggered()
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    QString file = QFileDialog::getOpenFileName(this, tr("Select bolt database..."));
 | 
					    QString file = QFileDialog::getOpenFileName(this, tr("Select bolt database..."));
 | 
				
			||||||
    if (file.length()) {
 | 
					    if (file.length()) {
 | 
				
			||||||
        openDatabase(file);
 | 
					        openDatabase(file, false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MainWindow::openDatabase(QString file)
 | 
					void MainWindow::on_actionOpen_database_as_read_only_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QString file = QFileDialog::getOpenFileName(this, tr("Select bolt database..."));
 | 
				
			||||||
 | 
					    if (file.length()) {
 | 
				
			||||||
 | 
					        openDatabase(file, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::openDatabase(QString file, bool readOnly)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // Open
 | 
					    // Open
 | 
				
			||||||
    QString error;
 | 
					    QString error;
 | 
				
			||||||
    auto *bdb = BoltDB::createFrom(file, error);
 | 
					    auto *bdb = BoltDB::createFrom(file, readOnly, error);
 | 
				
			||||||
    if (bdb == nullptr) {
 | 
					    if (bdb == nullptr) {
 | 
				
			||||||
        QMessageBox qmb;
 | 
					        QMessageBox qmb;
 | 
				
			||||||
        qmb.setText(tr("Error opening database: %1").arg(error));
 | 
					        qmb.setText(tr("Error opening database: %1").arg(error));
 | 
				
			||||||
@@ -78,12 +88,38 @@ void MainWindow::openDatabase(QString file)
 | 
				
			|||||||
    ui->bucketTree->expandItem(top);
 | 
					    ui->bucketTree->expandItem(top);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const QString getDisplayName(const QByteArray &qba) {
 | 
				
			||||||
 | 
					    // FIXME the formatting isn't so great when control characters, etc. are used
 | 
				
			||||||
 | 
					    // A C-style escape display, or the unicode-replacement-character would be preferable
 | 
				
			||||||
 | 
					    QString ret(QString::fromUtf8(qba));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool allPrintable = true;
 | 
				
			||||||
 | 
					    for (auto i = ret.begin(), e = ret.end(); i != e; ++i) {
 | 
				
			||||||
 | 
					        if (! i->isPrint()) {
 | 
				
			||||||
 | 
					            allPrintable = false;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (allPrintable) {
 | 
				
			||||||
 | 
					        return ret; // fine
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Some of the characters weren't printable.
 | 
				
			||||||
 | 
					    // Build up a replacement string
 | 
				
			||||||
 | 
					    QString replacement;
 | 
				
			||||||
 | 
					    for (auto i = ret.begin(), e = ret.end(); i != e; ++i) {
 | 
				
			||||||
 | 
					        replacement += i->isPrint() ? *i : QStringLiteral("\\u{%1}").arg(i->unicode());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return replacement;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MainWindow::refreshBucketTree(QTreeWidgetItem* itm)
 | 
					void MainWindow::refreshBucketTree(QTreeWidgetItem* itm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    QTreeWidgetItem *top = itm;
 | 
					    QTreeWidgetItem *top = itm;
 | 
				
			||||||
    QStringList browsePath;
 | 
					    QList<QByteArray> browsePath;
 | 
				
			||||||
    while(top->parent() != nullptr) {
 | 
					    while(top->parent() != nullptr) {
 | 
				
			||||||
        browsePath.push_front(top->text(0));
 | 
					        browsePath.push_front(top->data(0, BinaryDataRole).toByteArray());
 | 
				
			||||||
        top = top->parent();
 | 
					        top = top->parent();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -100,7 +136,8 @@ void MainWindow::refreshBucketTree(QTreeWidgetItem* itm)
 | 
				
			|||||||
        error,
 | 
					        error,
 | 
				
			||||||
        [=](QByteArray qba){
 | 
					        [=](QByteArray qba){
 | 
				
			||||||
            QTreeWidgetItem *child = new QTreeWidgetItem();
 | 
					            QTreeWidgetItem *child = new QTreeWidgetItem();
 | 
				
			||||||
            child->setText(0, QString::fromUtf8(qba));
 | 
					            child->setText(0, getDisplayName(qba));
 | 
				
			||||||
 | 
					            child->setData(0, BinaryDataRole, qba);
 | 
				
			||||||
            child->setIcon(0, QIcon(":/rsrc/table.png"));
 | 
					            child->setIcon(0, QIcon(":/rsrc/table.png"));
 | 
				
			||||||
            itm->addChild(child);
 | 
					            itm->addChild(child);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -215,10 +252,10 @@ void MainWindow::on_bucketTree_currentItemChanged(QTreeWidgetItem *current, QTre
 | 
				
			|||||||
        ui->stackedWidget->setCurrentWidget(ui->bucketPage);
 | 
					        ui->stackedWidget->setCurrentWidget(ui->bucketPage);
 | 
				
			||||||
        ui->bucketPropertiesArea->clear();
 | 
					        ui->bucketPropertiesArea->clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        QStringList browse;
 | 
					        QList<QByteArray> browse;
 | 
				
			||||||
        QTreeWidgetItem *top = current;
 | 
					        QTreeWidgetItem *top = current;
 | 
				
			||||||
        while (top->parent() != nullptr) {
 | 
					        while (top->parent() != nullptr) {
 | 
				
			||||||
            browse.push_front(top->text(0));
 | 
					            browse.push_front(top->data(0, BinaryDataRole).toByteArray());
 | 
				
			||||||
            top = top->parent();
 | 
					            top = top->parent();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        auto *bdb = GET_BDB(top);
 | 
					        auto *bdb = GET_BDB(top);
 | 
				
			||||||
@@ -242,14 +279,15 @@ void MainWindow::on_bucketTree_currentItemChanged(QTreeWidgetItem *current, QTre
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MainWindow::refreshData(BoltDB *bdb, QStringList browse)
 | 
					void MainWindow::refreshData(BoltDB *bdb, const QList<QByteArray>& browse)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // Load the data tab
 | 
					    // Load the data tab
 | 
				
			||||||
    ui->bucketData->clear();
 | 
					    ui->bucketData->clear();
 | 
				
			||||||
    QString err;
 | 
					    QString err;
 | 
				
			||||||
    bool ok = bdb->listKeys(browse, err, [=](QByteArray name, int64_t dataLen) {
 | 
					    bool ok = bdb->listKeys(browse, err, [=](QByteArray name, int64_t dataLen) {
 | 
				
			||||||
        auto *itm = new QTreeWidgetItem();
 | 
					        auto *itm = new QTreeWidgetItem();
 | 
				
			||||||
        itm->setText(0, QString::fromUtf8(name));
 | 
					        itm->setText(0, getDisplayName(name));
 | 
				
			||||||
 | 
					        itm->setData(0, BinaryDataRole, name);
 | 
				
			||||||
        itm->setText(1, QString("%1").arg(dataLen));
 | 
					        itm->setText(1, QString("%1").arg(dataLen));
 | 
				
			||||||
        ui->bucketData->addTopLevelItem(itm);
 | 
					        ui->bucketData->addTopLevelItem(itm);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -275,14 +313,14 @@ void MainWindow::on_actionClear_selection_triggered()
 | 
				
			|||||||
        return; \
 | 
					        return; \
 | 
				
			||||||
    } \
 | 
					    } \
 | 
				
			||||||
    QTreeWidgetItem* top = itm; \
 | 
					    QTreeWidgetItem* top = itm; \
 | 
				
			||||||
    QStringList browse; \
 | 
					    QList<QByteArray> browse; \
 | 
				
			||||||
    while(top->parent() != nullptr) { \
 | 
					    while(top->parent() != nullptr) { \
 | 
				
			||||||
        browse.push_front(top->text(0)); \
 | 
					        browse.push_front(top->data(0, BinaryDataRole).toByteArray()); \
 | 
				
			||||||
        top = top->parent(); \
 | 
					        top = top->parent(); \
 | 
				
			||||||
    } \
 | 
					    } \
 | 
				
			||||||
    auto *bdb = GET_BDB(top);
 | 
					    auto *bdb = GET_BDB(top);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MainWindow::openEditor(BoltDB *bdb, QStringList saveAs, QByteArray saveAsKey, QByteArray currentContent)
 | 
					void MainWindow::openEditor(BoltDB *bdb, const QList<QByteArray>& saveAs, QByteArray saveAsKey, QByteArray currentContent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto iw = new ItemWindow();
 | 
					    auto iw = new ItemWindow();
 | 
				
			||||||
    iw->ContentArea()->setPlainText(QString::fromUtf8(currentContent));
 | 
					    iw->ContentArea()->setPlainText(QString::fromUtf8(currentContent));
 | 
				
			||||||
@@ -312,15 +350,15 @@ void MainWindow::on_bucketData_doubleClicked(const QModelIndex &index)
 | 
				
			|||||||
    // Get item key
 | 
					    // Get item key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto model = index.model();
 | 
					    auto model = index.model();
 | 
				
			||||||
    QString key = model->data(model->index(index.row(), 0), 0).toString();
 | 
					    const QByteArray& key = model->data(model->index(index.row(), 0), BinaryDataRole).toByteArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // DB lookup
 | 
					    // DB lookup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bdb->getData(
 | 
					    bdb->getData(
 | 
				
			||||||
        browse,
 | 
					        browse,
 | 
				
			||||||
        key.toUtf8(),
 | 
					        key,
 | 
				
			||||||
        [=](QByteArray content) {
 | 
					        [=](QByteArray content) {
 | 
				
			||||||
            openEditor(bdb, browse, key.toUtf8(), content);
 | 
					            openEditor(bdb, browse, key, content);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [=](QString error) {
 | 
					        [=](QString error) {
 | 
				
			||||||
            QMessageBox qmb;
 | 
					            QMessageBox qmb;
 | 
				
			||||||
@@ -361,17 +399,27 @@ void MainWindow::on_actionDelete_bucket_triggered()
 | 
				
			|||||||
    GET_ITM_TOP_BROWSE_BDB;
 | 
					    GET_ITM_TOP_BROWSE_BDB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Prompt for confirmation
 | 
					    // Prompt for confirmation
 | 
				
			||||||
    QString bucketToDelete = itm->text(0);
 | 
					    const QByteArray& bucketToDelete = itm->data(0, BinaryDataRole).toByteArray();
 | 
				
			||||||
    if (QMessageBox::question(this, tr("Delete bucket"), tr("Are you sure you want to remove the bucket '%1'?").arg(bucketToDelete), QMessageBox::Yes, QMessageBox::Cancel) != QMessageBox::Yes) {
 | 
					    if (
 | 
				
			||||||
 | 
					        QMessageBox::question(
 | 
				
			||||||
 | 
					            this,
 | 
				
			||||||
 | 
					            tr("Delete bucket"),
 | 
				
			||||||
 | 
					            tr("Are you sure you want to remove the bucket '%1'?").arg(getDisplayName(bucketToDelete)),
 | 
				
			||||||
 | 
					            QMessageBox::Yes,
 | 
				
			||||||
 | 
					            QMessageBox::Cancel
 | 
				
			||||||
 | 
					        ) != QMessageBox::Yes
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QTreeWidgetItem* parent = itm->parent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // One level down
 | 
					    // One level down
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    browse.pop_back();
 | 
					    browse.pop_back();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    QString err;
 | 
					    QString err;
 | 
				
			||||||
    if (! bdb->deleteBucket(browse, bucketToDelete.toUtf8(), err)) {
 | 
					    if (! bdb->deleteBucket(browse, bucketToDelete, err)) {
 | 
				
			||||||
        QMessageBox qmb;
 | 
					        QMessageBox qmb;
 | 
				
			||||||
        qmb.setText(tr("Error removing bucket: %1").arg(err));
 | 
					        qmb.setText(tr("Error removing bucket: %1").arg(err));
 | 
				
			||||||
        qmb.exec();
 | 
					        qmb.exec();
 | 
				
			||||||
@@ -379,9 +427,9 @@ void MainWindow::on_actionDelete_bucket_triggered()
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Refresh bucket list
 | 
					    // Refresh bucket list
 | 
				
			||||||
    refreshBucketTree(itm->parent()); // sub-tree only
 | 
					    refreshBucketTree(parent); // sub-tree only
 | 
				
			||||||
    ui->bucketTree->expandItem(itm->parent());
 | 
					    ui->bucketTree->expandItem(parent);
 | 
				
			||||||
 | 
					    ui->bucketTree->setCurrentItem(parent);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MainWindow::on_AddDataButton_clicked()
 | 
					void MainWindow::on_AddDataButton_clicked()
 | 
				
			||||||
@@ -414,7 +462,7 @@ void MainWindow::on_DeleteDataButton_clicked()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    QString err;
 | 
					    QString err;
 | 
				
			||||||
    for (int i = selection.length(); i-->0;) {
 | 
					    for (int i = selection.length(); i-->0;) {
 | 
				
			||||||
        if (! bdb->deleteItem(browse, selection[i]->text(0).toUtf8(), err)) {
 | 
					        if (! bdb->deleteItem(browse, selection[i]->data(0, BinaryDataRole).toByteArray(), err)) {
 | 
				
			||||||
            QMessageBox qmb;
 | 
					            QMessageBox qmb;
 | 
				
			||||||
            qmb.setText(tr("Error removing item: %1").arg(err));
 | 
					            qmb.setText(tr("Error removing item: %1").arg(err));
 | 
				
			||||||
            qmb.exec();
 | 
					            qmb.exec();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,11 +51,13 @@ private slots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    void on_bucketData_itemSelectionChanged();
 | 
					    void on_bucketData_itemSelectionChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionOpen_database_as_read_only_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
    void openDatabase(QString file);
 | 
					    void openDatabase(QString file, bool readOnly);
 | 
				
			||||||
    void refreshBucketTree(QTreeWidgetItem* top);
 | 
					    void refreshBucketTree(QTreeWidgetItem* top);
 | 
				
			||||||
    void refreshData(BoltDB *bdb, QStringList browse);
 | 
					    void refreshData(BoltDB *bdb, const QList<QByteArray>& browse);
 | 
				
			||||||
    void openEditor(BoltDB *bdb, QStringList saveAs, QByteArray saveAsKey, QByteArray currentContent);
 | 
					    void openEditor(BoltDB *bdb, const QList<QByteArray>& saveAs, QByteArray saveAsKey, QByteArray currentContent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    Ui::MainWindow *ui;
 | 
					    Ui::MainWindow *ui;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -278,6 +278,7 @@
 | 
				
			|||||||
    </property>
 | 
					    </property>
 | 
				
			||||||
    <addaction name="actionNew_database"/>
 | 
					    <addaction name="actionNew_database"/>
 | 
				
			||||||
    <addaction name="actionOpen_database"/>
 | 
					    <addaction name="actionOpen_database"/>
 | 
				
			||||||
 | 
					    <addaction name="actionOpen_database_as_read_only"/>
 | 
				
			||||||
    <addaction name="separator"/>
 | 
					    <addaction name="separator"/>
 | 
				
			||||||
    <addaction name="actionExit"/>
 | 
					    <addaction name="actionExit"/>
 | 
				
			||||||
   </widget>
 | 
					   </widget>
 | 
				
			||||||
@@ -395,6 +396,11 @@
 | 
				
			|||||||
    <string>Add bucket...</string>
 | 
					    <string>Add bucket...</string>
 | 
				
			||||||
   </property>
 | 
					   </property>
 | 
				
			||||||
  </action>
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionOpen_database_as_read_only">
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>Open database as read-only...</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 </widget>
 | 
					 </widget>
 | 
				
			||||||
 <layoutdefault spacing="6" margin="11"/>
 | 
					 <layoutdefault spacing="6" margin="11"/>
 | 
				
			||||||
 <resources>
 | 
					 <resources>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,8 @@ win32: {
 | 
				
			|||||||
    DEFINES += CGO_WINDOWS
 | 
					    DEFINES += CGO_WINDOWS
 | 
				
			||||||
    QMAKE_LIBS += $$_PRO_FILE_PWD_/../build/win32/qbolt.a
 | 
					    QMAKE_LIBS += $$_PRO_FILE_PWD_/../build/win32/qbolt.a
 | 
				
			||||||
    QMAKE_LIBS += -lntdll
 | 
					    QMAKE_LIBS += -lntdll
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RC_ICONS = rsrc/qbolt.ico
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
linux: {
 | 
					linux: {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/qbolt.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								qbolt/rsrc/qbolt.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.1 KiB  | 
		Reference in New Issue
	
	Block a user