Compare commits
	
		
			79 Commits
		
	
	
		
			archive/co
			...
			v1.0.2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 99096b2360 | |||
| 0e92459779 | |||
| a13eaaf523 | |||
| dca8d277d3 | |||
| 15c739c0b9 | |||
| fc2da972ef | |||
| e45eea7111 | |||
| 1e62d79c07 | |||
| c74c5ae5c0 | |||
| b0092c4a6e | |||
| 5ce6368c4a | |||
| 3e7e54da4b | |||
| 9f80d687b2 | |||
| 96411e877f | |||
| ac7e078c02 | |||
| e13314f5dc | |||
| b50c3e738a | |||
| 54ad6015b7 | |||
| 40e84ac230 | |||
| 767eaa0a47 | |||
| bb594e768c | |||
| 516bd99c4d | |||
| 571bfcf4b6 | |||
| 26f7a11d80 | |||
| 21588021d3 | |||
| 7441e0c15b | |||
| 142f3f6bf4 | |||
| 19ddb1c956 | |||
| 97467eae4d | |||
| 17c37b6568 | |||
| 57237ef2e8 | |||
| 164f5a071d | |||
| ac46b16d0e | |||
| b9d114a2d8 | |||
| b368457247 | |||
| fb940d5f60 | |||
| 6d1e671e44 | |||
| b5149c4efa | |||
| f0f642b6b0 | |||
| 7c62abb352 | |||
| 4f1c48b55d | |||
| 37f9307db1 | |||
| 14e7ebb59c | |||
| 14510545d4 | |||
| 906cff2e57 | |||
| bf28495b1f | |||
| 98b78ab1e6 | |||
| 4394c8a50a | |||
| 9fb5bdad78 | |||
| d2ec12798e | |||
| 5270dd00bc | |||
| 35f28fa5ed | |||
| 5ea969e10c | |||
| d0becd0c3c | |||
| 1c81444645 | |||
| d7c3bfd1f5 | |||
| 0f1cc014d7 | |||
| 6ac8c3e67b | |||
| 546681a0ef | |||
| 679d1140cc | |||
| 60d71104e8 | |||
| 95a1bfeea5 | |||
| 8597f270f6 | |||
| d32ab82d73 | |||
| b6d1cafe54 | |||
| 3911d7d66c | |||
| be594cb1a5 | |||
| 8a93f163c4 | |||
| e82d0a5dd2 | |||
| 4b3fb783c4 | |||
| e8030a9579 | |||
| a94a330345 | |||
| 8ce03cbed6 | |||
| b76fad9512 | |||
| a9158c3a0c | |||
| dc31787526 | |||
| ff7484079e | |||
| 6e1f9ca1e1 | |||
| ac56f5e6c8 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					build-qbolt-*
 | 
				
			||||||
 | 
					dummy-data/dummy-data*
 | 
				
			||||||
 | 
					*.pro.user$
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
@@ -1,9 +0,0 @@
 | 
				
			|||||||
# Converted with codesite2git
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
project_name="qbolt"
 | 
					 | 
				
			||||||
short_description="A graphical database manager for BoltDB."
 | 
					 | 
				
			||||||
written_in_lang="C++ (Qt), Golang (CGo)"
 | 
					 | 
				
			||||||
topics=[]
 | 
					 | 
				
			||||||
ctime=1495324800
 | 
					 | 
				
			||||||
mtime=1497830400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										72
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					export PATH := /usr/lib/mxe/usr/bin:$(PATH)
 | 
				
			||||||
 | 
					GOFLAGS := -ldflags='-s -w' -gcflags='-trimpath=$(CURDIR)' -asmflags='-trimpath=$(CURDIR)'
 | 
				
			||||||
 | 
					VERSION := 1.0.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: all libs dist clean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					all: \
 | 
				
			||||||
 | 
						build/linux/qbolt \
 | 
				
			||||||
 | 
						build/win32/release/qbolt.exe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libs: \
 | 
				
			||||||
 | 
						build/linux/qbolt.a \
 | 
				
			||||||
 | 
						build/win32/qbolt.a
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					dist: \
 | 
				
			||||||
 | 
						build/dist/qbolt-${VERSION}-win32.zip \
 | 
				
			||||||
 | 
						build/dist/qbolt-${VERSION}-linux_amd64.tar.xz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					clean:
 | 
				
			||||||
 | 
						if [ -f qbolt/qbolt.a ] ; then rm qbolt/qbolt.a ; fi
 | 
				
			||||||
 | 
						if [ -f qbolt ] ; then rm qbolt ; fi
 | 
				
			||||||
 | 
						if [ -d build ] ; then rm -r build ; fi
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					# Build core golang shared library (linux)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					build/linux/qbolt.a: *.go
 | 
				
			||||||
 | 
						mkdir -p build/linux
 | 
				
			||||||
 | 
						go build ${GOFLAGS} -buildmode=c-archive -o build/linux/qbolt.a
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					# Build core golang shared library (win32)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					build/win32/qbolt.a: *.go
 | 
				
			||||||
 | 
						mkdir -p build/win32
 | 
				
			||||||
 | 
						CC=/usr/lib/mxe/usr/bin/i686-w64-mingw32.static-gcc CGO_ENABLED=1 GOARCH=386 GOOS=windows \
 | 
				
			||||||
 | 
							go build ${GOFLAGS} -buildmode=c-archive -o build/win32/qbolt.a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Linux binaries
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					build/linux/qbolt: build/linux/qbolt.a qbolt/*
 | 
				
			||||||
 | 
						cd build/linux && qmake ../../qbolt/qbolt.pro && make
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Linux distribution
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build/dist/qbolt-${VERSION}-linux_amd64.tar.xz: build/linux/qbolt
 | 
				
			||||||
 | 
						XZ_OPTS=-9 tar caf build/dist/qbolt-${VERSION}-linux_amd64.tar.xz -C build/linux qbolt --owner=0 --group=0
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					# Windows binaries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build/win32/release/qbolt.exe: build/win32/qbolt.a qbolt/*
 | 
				
			||||||
 | 
						cd build/win32 && i686-w64-mingw32.static-qmake-qt5 ../../qbolt/qbolt.pro && make
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Dockerized Windows build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: build-docker-build-environment
 | 
				
			||||||
 | 
					build-docker-build-environment:
 | 
				
			||||||
 | 
						cd docker && docker build -t win32-cross-qt-mxe:latest -f win32-cross-qt-mxe.Dockerfile 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: build-windows-in-docker
 | 
				
			||||||
 | 
					build-windows-in-docker:
 | 
				
			||||||
 | 
						docker run --rm -v $(CURDIR):/qbolt win32-cross-qt-mxe:latest /bin/sh -c 'cd /qbolt && make build/win32/release/qbolt.exe'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Windows distribution
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build/win32/dist/qbolt.exe: build/win32/release/qbolt.exe
 | 
				
			||||||
 | 
						mkdir -p build/win32/dist
 | 
				
			||||||
 | 
						cp build/win32/release/qbolt.exe build/win32/dist/qbolt.exe
 | 
				
			||||||
 | 
					#	upx --lzma build/win32/dist/qbolt.exe
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					build/dist/qbolt-${VERSION}-win32.zip: build/win32/dist/qbolt.exe
 | 
				
			||||||
 | 
						mkdir -p build/dist
 | 
				
			||||||
 | 
						zip -0 -j build/dist/qbolt-${VERSION}-win32.zip build/win32/dist/qbolt.exe
 | 
				
			||||||
							
								
								
									
										56
									
								
								MemoryStore.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "C"
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ObjectReference int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var NullObjectReference error = errors.New("Null object reference")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GoMemoryStore is a int->interface storage structure so that Go pointers are
 | 
				
			||||||
 | 
					// never exposed to C code.
 | 
				
			||||||
 | 
					type GoMemoryStore struct {
 | 
				
			||||||
 | 
						mtx   sync.RWMutex
 | 
				
			||||||
 | 
						items map[int64]interface{}
 | 
				
			||||||
 | 
						next  int64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewGoMemoryStore() *GoMemoryStore {
 | 
				
			||||||
 | 
						ret := GoMemoryStore{}
 | 
				
			||||||
 | 
						ret.items = make(map[int64]interface{})
 | 
				
			||||||
 | 
						return &ret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *GoMemoryStore) Put(itm interface{}) ObjectReference {
 | 
				
			||||||
 | 
						this.mtx.Lock()
 | 
				
			||||||
 | 
						defer this.mtx.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						key := this.next
 | 
				
			||||||
 | 
						this.items[key] = itm
 | 
				
			||||||
 | 
						this.next++
 | 
				
			||||||
 | 
						return ObjectReference(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *GoMemoryStore) Get(i ObjectReference) (interface{}, bool) {
 | 
				
			||||||
 | 
						this.mtx.RLock()
 | 
				
			||||||
 | 
						defer this.mtx.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret, ok := this.items[int64(i)]
 | 
				
			||||||
 | 
						return ret, ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *GoMemoryStore) Delete(i ObjectReference) {
 | 
				
			||||||
 | 
						this.mtx.Lock()
 | 
				
			||||||
 | 
						defer this.mtx.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						delete(this.items, int64(i))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var gms *GoMemoryStore = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						gms = NewGoMemoryStore()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@@ -1,13 +1,13 @@
 | 
				
			|||||||
# qbolt
 | 
					# qbolt
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A graphical database manager for BoltDB.
 | 
					A graphical database manager for BoltDB.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QBolt allows you to graphically view and edit the content of Bolt databases.
 | 
					QBolt allows you to graphically view and edit the content of Bolt databases.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The project consists of two parts; a C binding (CGo) for the embeddable Bolt database engine, and a graphical interface built in C++/Qt that links to it.
 | 
					The project consists of two parts; a C binding (CGo) for the embeddable Bolt database engine, and a graphical interface built in C++/Qt that links to it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Written in C++ (Qt), Golang (CGo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Features
 | 
					## Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Open existing database or create new database
 | 
					- Open existing database or create new database
 | 
				
			||||||
@@ -24,25 +24,26 @@ Source code content of `qbolt-x.x.x-src.tar.gz` is released under the ISC licens
 | 
				
			|||||||
BoltDB is released under the MIT license.
 | 
					BoltDB is released under the MIT license.
 | 
				
			||||||
The Windows binary is released under LGPL-3+ owing to the static copy of Qt.
 | 
					The Windows binary is released under LGPL-3+ owing to the static copy of Qt.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## See Also
 | 
					## See also
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- BoltDB https://github.com/boltdb/bolt
 | 
					- BoltDB https://github.com/boltdb/bolt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Changelog
 | 
					## Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2020-04-12 1.0.2
 | 
				
			||||||
 | 
					- Rebuild artefacts with etcd-io/bbolt v1.3.5, go 1.15, Qt 5.15, and new GCC versions
 | 
				
			||||||
 | 
					- Switch from hg to Git
 | 
				
			||||||
 | 
					- Use Go modules
 | 
				
			||||||
 | 
					- Add support for building Windows binary in Docker
 | 
				
			||||||
 | 
					- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2017-06-19 1.0.1
 | 
					2017-06-19 1.0.1
 | 
				
			||||||
- Feature: Option to open database as read-only
 | 
					- 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 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 an issue with crashing when deleting a bucket other than the selected one
 | 
				
			||||||
- Fix a cosmetic issue with application icon on Windows
 | 
					- Fix a cosmetic issue with application icon on Windows
 | 
				
			||||||
- [⬇️ qbolt-1.0.1-win32.zip](dist-archive/qbolt-1.0.1-win32.zip) *(5.07 MiB)*
 | 
					- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.1)
 | 
				
			||||||
- [⬇️ qbolt-1.0.1-src.tar.gz](dist-archive/qbolt-1.0.1-src.tar.gz) *(265.88 KiB)*
 | 
					 | 
				
			||||||
- [⬇️ qbolt-1.0.1-linux_amd64.tar.xz](dist-archive/qbolt-1.0.1-linux_amd64.tar.xz) *(584.88 KiB)*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
2017-05-21 1.0.0
 | 
					2017-05-21 1.0.0
 | 
				
			||||||
- Initial public release
 | 
					- Initial public release
 | 
				
			||||||
- [⬇️ qbolt-1.0.0-win32.zip](dist-archive/qbolt-1.0.0-win32.zip) *(5.07 MiB)*
 | 
					- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.0)
 | 
				
			||||||
- [⬇️ qbolt-1.0.0-src.tar.gz](dist-archive/qbolt-1.0.0-src.tar.gz) *(221.91 KiB)*
 | 
					 | 
				
			||||||
- [⬇️ qbolt-1.0.0-linux_amd64.tar.xz](dist-archive/qbolt-1.0.0-linux_amd64.tar.xz) *(584.62 KiB)*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB  | 
| 
		 Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB  | 
| 
		 Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB  | 
| 
		 Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB  | 
							
								
								
									
										13
									
								
								docker/win32-cross-qt-mxe.Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					FROM debian:bullseye
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
 | 
				
			||||||
 | 
					    apt-get install -qyy gnupg2 golang-go ca-certificates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN DEBIAN_FRONTEND=noninteractive \
 | 
				
			||||||
 | 
					    echo "deb https://pkg.mxe.cc/repos/apt buster main" >/etc/apt/sources.list.d/mxeapt.list && \
 | 
				
			||||||
 | 
					    apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 86B72ED9 && \
 | 
				
			||||||
 | 
					    apt-get update && \
 | 
				
			||||||
 | 
					    apt-get install -qyy mxe-i686-w64-mingw32.static-qt5 && \
 | 
				
			||||||
 | 
					    apt-get clean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENV PATH=/usr/lib/mxe/usr/bin:$PATH
 | 
				
			||||||
							
								
								
									
										83
									
								
								dummy-data/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						//"fmt"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bolt "github.com/boltdb/bolt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func random_name() string {
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
						// fill with some basic items
 | 
				
			||||||
 | 
						for i := 0; i < 30; i += 1 {
 | 
				
			||||||
 | 
							err := bucket.Put([]byte(random_name()), []byte("SAMPLE CONTENT "+random_name()))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 1/20 (5%) chance of recursion
 | 
				
			||||||
 | 
						if rand.Intn(100) >= 95 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i := 0; i < 5; i += 1 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								child, err := bucket.CreateBucket([]byte(random_name()))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = fill_bucket(tx, child)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						db, err := bolt.Open("sample.db", 0644, bolt.DefaultOptions)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.Update(func(tx *bolt.Tx) error {
 | 
				
			||||||
 | 
							// top-level buckets
 | 
				
			||||||
 | 
							for i := 0; i < 50; i += 1 {
 | 
				
			||||||
 | 
								bucketName := random_name()
 | 
				
			||||||
 | 
								bucket, err := tx.CreateBucket([]byte(bucketName))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = fill_bucket(tx, bucket)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.Close()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os.Exit(0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					module code.ivysaur.me/qbolt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require github.com/boltdb/bolt v1.3.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					replace github.com/boltdb/bolt => go.etcd.io/bbolt v1.3.5
 | 
				
			||||||
							
								
								
									
										5
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
 | 
				
			||||||
 | 
					github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
 | 
				
			||||||
 | 
					go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
 | 
				
			||||||
 | 
					go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
							
								
								
									
										398
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,398 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "C"
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bolt "github.com/boltdb/bolt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ERROR_AND_STOP_CALLING int64 = 100
 | 
				
			||||||
 | 
						ERROR_AND_KEEP_CALLING       = 101
 | 
				
			||||||
 | 
						FINISHED_OK                  = 102
 | 
				
			||||||
 | 
						REAL_MESSAGE                 = 103
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Magic int64 = 0x10203040
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export GetMagic
 | 
				
			||||||
 | 
					func GetMagic() int64 {
 | 
				
			||||||
 | 
						return Magic
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_Open
 | 
				
			||||||
 | 
					func Bolt_Open(readOnly bool, path string) (ObjectReference, *C.char, int) {
 | 
				
			||||||
 | 
						opts := *bolt.DefaultOptions
 | 
				
			||||||
 | 
						opts.Timeout = 10 * time.Second
 | 
				
			||||||
 | 
						opts.ReadOnly = readOnly
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ptrDB, err := bolt.Open(path, os.FileMode(0644), &opts)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							errMsg := err.Error()
 | 
				
			||||||
 | 
							return 0, C.CString(errMsg), len(errMsg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbRef := gms.Put(ptrDB)
 | 
				
			||||||
 | 
						return dbRef, nil, 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func withBoltDBReference(b ObjectReference, fn func(db *bolt.DB) error) error {
 | 
				
			||||||
 | 
						dbIFC, ok := gms.Get(b)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return NullObjectReference
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ptrDB, ok := dbIFC.(*bolt.DB)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return NullObjectReference
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return fn(ptrDB)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func walkBuckets(tx *bolt.Tx, browse []string) (*bolt.Bucket, error) {
 | 
				
			||||||
 | 
						bucket := tx.Bucket([]byte(browse[0]))
 | 
				
			||||||
 | 
						if bucket == nil {
 | 
				
			||||||
 | 
							return nil, errors.New("Unknown bucket")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 1; i < len(browse); i += 1 {
 | 
				
			||||||
 | 
							bucket = bucket.Bucket([]byte(browse[i]))
 | 
				
			||||||
 | 
							if bucket == nil {
 | 
				
			||||||
 | 
								return nil, errors.New("Unknown bucket")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bucket, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func withBrowse_ReadOnly(b_ref ObjectReference, browse []string, fn func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error) error {
 | 
				
			||||||
 | 
						if len(browse) == 0 {
 | 
				
			||||||
 | 
							// not a bucket
 | 
				
			||||||
 | 
							return errors.New("No bucket selected")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return withBoltDBReference(b_ref, func(db *bolt.DB) error {
 | 
				
			||||||
 | 
							return db.View(func(tx *bolt.Tx) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bucket, err := walkBuckets(tx, browse)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Walked the bucket chain, now run the user callback
 | 
				
			||||||
 | 
								return fn(db, tx, bucket)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func err2triple(err error) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							msg := err.Error()
 | 
				
			||||||
 | 
							return ERROR_AND_STOP_CALLING, C.CString(msg), len(msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return FINISHED_OK, nil, 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_CreateBucket
 | 
				
			||||||
 | 
					func Bolt_CreateBucket(b_ref ObjectReference, browse []string, newBucket string) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
 | 
				
			||||||
 | 
							return db.Update(func(tx *bolt.Tx) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if len(browse) == 0 {
 | 
				
			||||||
 | 
									// Top-level bucket
 | 
				
			||||||
 | 
									_, err := tx.CreateBucket([]byte(newBucket))
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Deeper bucket
 | 
				
			||||||
 | 
									bucket, err := walkBuckets(tx, browse)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Walked the bucket chain, now create the new bucket
 | 
				
			||||||
 | 
									_, err = bucket.CreateBucket([]byte(newBucket))
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err2triple(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_DeleteBucket
 | 
				
			||||||
 | 
					func Bolt_DeleteBucket(b_ref ObjectReference, browse []string, delBucket string) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
 | 
				
			||||||
 | 
							return db.Update(func(tx *bolt.Tx) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if len(browse) == 0 {
 | 
				
			||||||
 | 
									// Top-level bucket
 | 
				
			||||||
 | 
									return tx.DeleteBucket([]byte(delBucket))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Deeper bucket
 | 
				
			||||||
 | 
									bucket, err := walkBuckets(tx, browse)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Walked the bucket chain, now delete the selected bucket
 | 
				
			||||||
 | 
									return bucket.DeleteBucket([]byte(delBucket))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err2triple(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_SetItem
 | 
				
			||||||
 | 
					func Bolt_SetItem(b_ref ObjectReference, browse []string, key, val string) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						if len(browse) == 0 {
 | 
				
			||||||
 | 
							return err2triple(errors.New("Can't create top-level items"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
 | 
				
			||||||
 | 
							return db.Update(func(tx *bolt.Tx) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bucket, err := walkBuckets(tx, browse)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return bucket.Put([]byte(key), []byte(val))
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err2triple(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_DeleteItem
 | 
				
			||||||
 | 
					func Bolt_DeleteItem(b_ref ObjectReference, browse []string, key string) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						if len(browse) == 0 {
 | 
				
			||||||
 | 
							return err2triple(errors.New("Can't create top-level items"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
 | 
				
			||||||
 | 
							return db.Update(func(tx *bolt.Tx) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bucket, err := walkBuckets(tx, browse)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return bucket.Delete([]byte(key))
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err2triple(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CallResponse struct {
 | 
				
			||||||
 | 
						s string
 | 
				
			||||||
 | 
						e error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_DBStats
 | 
				
			||||||
 | 
					func Bolt_DBStats(b ObjectReference) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						var stats bolt.Stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := withBoltDBReference(b, func(db *bolt.DB) error {
 | 
				
			||||||
 | 
							stats = db.Stats()
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jBytes, err := json.Marshal(stats)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err2triple(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_BucketStats
 | 
				
			||||||
 | 
					func Bolt_BucketStats(b ObjectReference, browse []string) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						var stats bolt.BucketStats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error {
 | 
				
			||||||
 | 
							stats = bucket.Stats()
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err2triple(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jBytes, err := json.Marshal(stats)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err2triple(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type NextCall struct {
 | 
				
			||||||
 | 
						content chan CallResponse
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_ListBuckets
 | 
				
			||||||
 | 
					func Bolt_ListBuckets(b ObjectReference, browse []string) ObjectReference {
 | 
				
			||||||
 | 
						pNC := &NextCall{
 | 
				
			||||||
 | 
							content: make(chan CallResponse, 0),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pNC_Ref := gms.Put(pNC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(browse) == 0 {
 | 
				
			||||||
 | 
								// root mode
 | 
				
			||||||
 | 
								err = withBoltDBReference(b, func(db *bolt.DB) error {
 | 
				
			||||||
 | 
									return db.View(func(tx *bolt.Tx) error {
 | 
				
			||||||
 | 
										return tx.ForEach(func(k []byte, _ *bolt.Bucket) error {
 | 
				
			||||||
 | 
											pNC.content <- CallResponse{s: string(k)}
 | 
				
			||||||
 | 
											return nil
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Nested-mode
 | 
				
			||||||
 | 
								err = withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error {
 | 
				
			||||||
 | 
									return bucket.ForEach(func(k, v []byte) error {
 | 
				
			||||||
 | 
										// non-nil v means it's a data item
 | 
				
			||||||
 | 
										if v == nil {
 | 
				
			||||||
 | 
											pNC.content <- CallResponse{s: string(k)}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										return nil
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								pNC.content <- CallResponse{e: err}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							close(pNC.content)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pNC_Ref
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_ListItems
 | 
				
			||||||
 | 
					func Bolt_ListItems(b ObjectReference, browse []string) ObjectReference {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pNC := &NextCall{
 | 
				
			||||||
 | 
							content: make(chan CallResponse, 0),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pNC_Ref := gms.Put(pNC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(browse) == 0 {
 | 
				
			||||||
 | 
								err = errors.New("No bucket specified")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Nested-mode
 | 
				
			||||||
 | 
								err = withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error {
 | 
				
			||||||
 | 
									return bucket.ForEach(func(k, v []byte) error {
 | 
				
			||||||
 | 
										if v == nil {
 | 
				
			||||||
 | 
											return nil // nil v means it's a bucket, skip
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										itemLength := make([]byte, 8)
 | 
				
			||||||
 | 
										binary.LittleEndian.PutUint64(itemLength, uint64(len(v)))
 | 
				
			||||||
 | 
										pNC.content <- CallResponse{s: string(itemLength) + string(k)}
 | 
				
			||||||
 | 
										return nil
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								pNC.content <- CallResponse{e: err}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							close(pNC.content)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pNC_Ref
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_GetItem
 | 
				
			||||||
 | 
					func Bolt_GetItem(b ObjectReference, browse []string, key string) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						var ret *C.char = nil
 | 
				
			||||||
 | 
						var ret_len = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error {
 | 
				
			||||||
 | 
							d := bucket.Get([]byte(key))
 | 
				
			||||||
 | 
							ret = C.CString(string(d))
 | 
				
			||||||
 | 
							ret_len = len(d)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err2triple(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return REAL_MESSAGE, ret, ret_len
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export GetNext
 | 
				
			||||||
 | 
					func GetNext(oRef ObjectReference) (int64, *C.char, int) {
 | 
				
			||||||
 | 
						pNC_Iface, ok := gms.Get(oRef)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return err2triple(NullObjectReference)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pNC, ok := pNC_Iface.(*NextCall)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return err2triple(NullObjectReference)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cr, ok := <-pNC.content
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							gms.Delete(oRef)
 | 
				
			||||||
 | 
							return err2triple(nil)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if cr.e != nil {
 | 
				
			||||||
 | 
							msg := cr.e.Error()
 | 
				
			||||||
 | 
							return ERROR_AND_KEEP_CALLING, C.CString(msg), len(msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return REAL_MESSAGE, C.CString(cr.s), len(cr.s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_ListBucketsAtRoot
 | 
				
			||||||
 | 
					func Bolt_ListBucketsAtRoot(b ObjectReference) ObjectReference {
 | 
				
			||||||
 | 
						return Bolt_ListBuckets(b, nil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export Bolt_Close
 | 
				
			||||||
 | 
					func Bolt_Close(b ObjectReference) (*C.char, int) {
 | 
				
			||||||
 | 
						err := withBoltDBReference(b, func(db *bolt.DB) error {
 | 
				
			||||||
 | 
							return db.Close()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							msg := err.Error()
 | 
				
			||||||
 | 
							return C.CString(msg), len(msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gms.Delete(b)
 | 
				
			||||||
 | 
						return nil, 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						// virtual
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										219
									
								
								qbolt/boltdb.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,219 @@
 | 
				
			|||||||
 | 
					#include "boltdb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QtEndian>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BoltDB::BoltDB()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BoltDB* BoltDB::createFrom(QString filePath, bool readOnly, QString &errorOut)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QByteArray filePathBytes(filePath.toUtf8());
 | 
				
			||||||
 | 
					    GoString filePathGS = Interop::toGoString_WeakRef(&filePathBytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto open_ret = ::Bolt_Open(readOnly, filePathGS);
 | 
				
			||||||
 | 
					    if (open_ret.r2 != 0) {
 | 
				
			||||||
 | 
					        errorOut = QString::fromUtf8(open_ret.r1, open_ret.r2);
 | 
				
			||||||
 | 
					        free(open_ret.r1);
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BoltDB *ret = new BoltDB();
 | 
				
			||||||
 | 
					    ret->gmsDbRef = open_ret.r0;
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const int ERROR_AND_STOP_CALLING = 100;
 | 
				
			||||||
 | 
					static const int ERROR_AND_KEEP_CALLING = 101;
 | 
				
			||||||
 | 
					static const int FINISHED_OK            = 102;
 | 
				
			||||||
 | 
					static const int REAL_MESSAGE           = 103;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool handleTriple(int r0, char* r1, int64_t r2, QString& errorOut) {
 | 
				
			||||||
 | 
					    if (r0 == ERROR_AND_STOP_CALLING) {
 | 
				
			||||||
 | 
					        errorOut = QString::fromUtf8(r1, r2);
 | 
				
			||||||
 | 
					        free(r1);
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if (r0 == FINISHED_OK) {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // ?? unreachable
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::addBucket(const QList<QByteArray>& bucketPath, QByteArray bucketName, QString& errorOut)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
 | 
					    GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
 | 
				
			||||||
 | 
					    auto resp = ::Bolt_CreateBucket(this->gmsDbRef, browse.slice, bucketNameGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::deleteBucket(const QList<QByteArray>& bucketPath, QByteArray bucketName, QString& errorOut)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
 | 
					    GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
 | 
				
			||||||
 | 
					    auto resp = ::Bolt_DeleteBucket(this->gmsDbRef, browse.slice, bucketNameGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::setItem(const QList<QByteArray>& bucketPath, QByteArray keyName, QByteArray value, QString& errorOut)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
 | 
					    GoString keyNameGS = Interop::toGoString_WeakRef(&keyName);
 | 
				
			||||||
 | 
					    GoString valueGS = Interop::toGoString_WeakRef(&value);
 | 
				
			||||||
 | 
					    auto resp = ::Bolt_SetItem(this->gmsDbRef, browse.slice, keyNameGS, valueGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::deleteItem(const QList<QByteArray>& bucketPath, QByteArray keyName, QString& errorOut)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
 | 
					    GoString keyNameGS = Interop::toGoString_WeakRef(&keyName);
 | 
				
			||||||
 | 
					    auto resp = ::Bolt_DeleteItem(this->gmsDbRef, browse.slice, keyNameGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::listBucketsAtRoot(QString& errorOut, NameReciever cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto listJob = ::Bolt_ListBucketsAtRoot(this->gmsDbRef);
 | 
				
			||||||
 | 
					    return pumpNext(listJob, errorOut, cb);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::listBuckets(const QList<QByteArray>& bucketPath, QString& errorOut, NameReciever cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (bucketPath.size() == 0) {
 | 
				
			||||||
 | 
					        return listBucketsAtRoot(errorOut, cb);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
 | 
					    auto listJob = ::Bolt_ListBuckets(this->gmsDbRef, browse.slice);
 | 
				
			||||||
 | 
					    return pumpNext(listJob, errorOut, cb);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::listKeys(const QList<QByteArray>& bucketPath, QString& errorOut, std::function<void(QByteArray, int64_t)> cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
 | 
					    auto listJob = ::Bolt_ListItems(this->gmsDbRef, browse.slice);
 | 
				
			||||||
 | 
					    return pumpNext(listJob, errorOut, [=](QByteArray b) {
 | 
				
			||||||
 | 
					        // First 8 bytes are little-endian uint64 len
 | 
				
			||||||
 | 
					        int64_t dataLen = qFromLittleEndian<qint64>(b.mid(0, 8));
 | 
				
			||||||
 | 
					        cb(b.mid(8), dataLen);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::getData(const QList<QByteArray>& bucketPath, QByteArray key, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper browse(bucketPath);
 | 
				
			||||||
 | 
					    GoString keyGS = Interop::toGoString_WeakRef(&key);
 | 
				
			||||||
 | 
					    auto resp = ::Bolt_GetItem(this->gmsDbRef, browse.slice, keyGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (resp.r0 == ERROR_AND_STOP_CALLING) {
 | 
				
			||||||
 | 
					        onError(QString::fromUtf8(resp.r1, resp.r2));
 | 
				
			||||||
 | 
					        free(resp.r1);
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if (resp.r0 == REAL_MESSAGE) {
 | 
				
			||||||
 | 
					        onSuccess(QByteArray(resp.r1, resp.r2));
 | 
				
			||||||
 | 
					        free(resp.r1);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // ?? unreachable
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::pumpNext(GoInt64 jobRef, QString& errorOut, NameReciever cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    errorOut.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(;;) {
 | 
				
			||||||
 | 
					        auto gnr = ::GetNext(jobRef);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (gnr.r0 == ERROR_AND_STOP_CALLING) {
 | 
				
			||||||
 | 
					            errorOut.append(QString::fromUtf8(gnr.r1, gnr.r2)); // log error
 | 
				
			||||||
 | 
					            free(gnr.r1);
 | 
				
			||||||
 | 
					            break; // done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if (gnr.r0 == ERROR_AND_KEEP_CALLING) {
 | 
				
			||||||
 | 
					            errorOut.append(QString::fromUtf8(gnr.r1, gnr.r2)); // log error
 | 
				
			||||||
 | 
					            free(gnr.r1);
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if (gnr.r0 == FINISHED_OK) {
 | 
				
			||||||
 | 
					            // Once we hit this, the go-side will clean up the channel / associated goroutines
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if (gnr.r0 == REAL_MESSAGE) {
 | 
				
			||||||
 | 
					            cb(QByteArray(gnr.r1, gnr.r2));
 | 
				
			||||||
 | 
					            free(gnr.r1);
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (errorOut.length() == 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::getStatsJSON(std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto statresp = Bolt_DBStats(this->gmsDbRef);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (statresp.r0 == ERROR_AND_STOP_CALLING) {
 | 
				
			||||||
 | 
					        onError(QString::fromUtf8(statresp.r1, statresp.r2));
 | 
				
			||||||
 | 
					        free(statresp.r1);
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if (statresp.r0 == REAL_MESSAGE) {
 | 
				
			||||||
 | 
					        onSuccess(QByteArray(statresp.r1, statresp.r2));
 | 
				
			||||||
 | 
					        free(statresp.r1);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // ?? shouldn't be reachable
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BoltDB::getBucketStatsJSON(const QList<QByteArray>& bucketPath, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper sliceWrapper(bucketPath);
 | 
				
			||||||
 | 
					    auto statresp = Bolt_BucketStats(this->gmsDbRef, sliceWrapper.slice);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (statresp.r0 == ERROR_AND_STOP_CALLING) {
 | 
				
			||||||
 | 
					        QString err = QString::fromUtf8(statresp.r1, statresp.r2);
 | 
				
			||||||
 | 
					        free(statresp.r1);
 | 
				
			||||||
 | 
					        onError(err);
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if (statresp.r0 == REAL_MESSAGE) {
 | 
				
			||||||
 | 
					        onSuccess(QByteArray(statresp.r1, statresp.r2));
 | 
				
			||||||
 | 
					        free(statresp.r1);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // ?? shouldn't be reachable
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BoltDB::~BoltDB()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto err = ::Bolt_Close(this->gmsDbRef);
 | 
				
			||||||
 | 
					    if (err.r1 != 0) {
 | 
				
			||||||
 | 
					        // Error closing database!
 | 
				
			||||||
 | 
					        // Need to display an alert... somewhere
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        free(err.r0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								qbolt/boltdb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					#ifndef BOLTDB_H
 | 
				
			||||||
 | 
					#define BOLTDB_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "interop.h"
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef std::function<void(QByteArray)> NameReciever;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BoltDB
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    BoltDB();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GoInt64 gmsDbRef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    static BoltDB* createFrom(QString filePath, bool readOnly, QString &errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool listBucketsAtRoot(QString& errorOut, NameReciever cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool listBuckets(const QList<QByteArray>& bucketPath, QString& errorOut, NameReciever cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool addBucket(const QList<QByteArray>& bucketPath, QByteArray bucketName, QString& errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool deleteBucket(const QList<QByteArray>& bucketPath, QByteArray bucketName, QString& errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool setItem(const QList<QByteArray>& bucketPath, QByteArray keyName, QByteArray value, QString& errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool deleteItem(const QList<QByteArray>& bucketPath, QByteArray keyName, QString& errorOut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool listKeys(const QList<QByteArray>& bucketPath, QString& errorOut, std::function<void(QByteArray, int64_t)> cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 getBucketStatsJSON(const QList<QByteArray>& bucketPath, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ~BoltDB();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool pumpNext(GoInt64 jobRef, QString& errorOut, NameReciever cb);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // BOLTDB_H
 | 
				
			||||||
							
								
								
									
										42
									
								
								qbolt/interop.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					#include "interop.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QStringList>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Interop::Interop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GoString Interop::toGoString_WeakRef(QByteArray *qba) {
 | 
				
			||||||
 | 
					    return GoString{qba->data(), qba->length()};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int64_t Interop::GetMagic() {
 | 
				
			||||||
 | 
					    return ::GetMagic();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GoSliceManagedWrapper::GoSliceManagedWrapper(const QList<QByteArray>& qsl) :
 | 
				
			||||||
 | 
					    rawStrings(),
 | 
				
			||||||
 | 
					    slice(),
 | 
				
			||||||
 | 
					    strings(nullptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    rawStrings.reserve(qsl.size());
 | 
				
			||||||
 | 
					    strings = new GoString[qsl.size()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = 0; i < qsl.size(); ++i) {
 | 
				
			||||||
 | 
					        rawStrings.push_back( qsl.at(i) );
 | 
				
			||||||
 | 
					        strings[i].p = rawStrings[i].data();
 | 
				
			||||||
 | 
					        strings[i].n = rawStrings[i].size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    slice.data = static_cast<void*>(strings);
 | 
				
			||||||
 | 
					    slice.len  = qsl.size(); //  * sizeof(GoString);
 | 
				
			||||||
 | 
					    slice.cap  = slice.len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GoSliceManagedWrapper::~GoSliceManagedWrapper()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    delete[] strings;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								qbolt/interop.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					#ifndef INTEROP_H
 | 
				
			||||||
 | 
					#define INTEROP_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qbolt_cgo.h"
 | 
				
			||||||
 | 
					#include <QString>
 | 
				
			||||||
 | 
					#include <QList>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GoSliceManagedWrapper {
 | 
				
			||||||
 | 
					    Q_DISABLE_COPY(GoSliceManagedWrapper)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    GoSliceManagedWrapper(const QList<QByteArray>& qsl);
 | 
				
			||||||
 | 
					    ~GoSliceManagedWrapper();
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    QList<QByteArray> rawStrings;
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    GoSlice slice;
 | 
				
			||||||
 | 
					    GoString *strings;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Interop
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    Interop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static GoString toGoString_WeakRef(QByteArray *qba);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static int64_t GetMagic();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // INTEROP_H
 | 
				
			||||||
							
								
								
									
										18
									
								
								qbolt/itemwindow.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					#include "itemwindow.h"
 | 
				
			||||||
 | 
					#include "ui_itemwindow.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ItemWindow::ItemWindow(QWidget *parent) :
 | 
				
			||||||
 | 
					    QDialog(parent),
 | 
				
			||||||
 | 
					    ui(new Ui::ItemWindow)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ui->setupUi(this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ItemWindow::~ItemWindow()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    delete ui;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QPlainTextEdit* ItemWindow::ContentArea() const {
 | 
				
			||||||
 | 
					    return ui->contentArea;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								qbolt/itemwindow.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					#ifndef ITEMWINDOW_H
 | 
				
			||||||
 | 
					#define ITEMWINDOW_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QDialog>
 | 
				
			||||||
 | 
					#include <QPlainTextEdit>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ui {
 | 
				
			||||||
 | 
					class ItemWindow;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ItemWindow : public QDialog
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit ItemWindow(QWidget *parent = 0);
 | 
				
			||||||
 | 
					    ~ItemWindow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QPlainTextEdit* ContentArea() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    Ui::ItemWindow *ui;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // ITEMWINDOW_H
 | 
				
			||||||
							
								
								
									
										101
									
								
								qbolt/itemwindow.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<ui version="4.0">
 | 
				
			||||||
 | 
					 <class>ItemWindow</class>
 | 
				
			||||||
 | 
					 <widget class="QDialog" name="ItemWindow">
 | 
				
			||||||
 | 
					  <property name="geometry">
 | 
				
			||||||
 | 
					   <rect>
 | 
				
			||||||
 | 
					    <x>0</x>
 | 
				
			||||||
 | 
					    <y>0</y>
 | 
				
			||||||
 | 
					    <width>370</width>
 | 
				
			||||||
 | 
					    <height>353</height>
 | 
				
			||||||
 | 
					   </rect>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
 | 
					  <property name="windowTitle">
 | 
				
			||||||
 | 
					   <string/>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
 | 
					  <property name="windowIcon">
 | 
				
			||||||
 | 
					   <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					    <normaloff>:/rsrc/database_lightning.png</normaloff>:/rsrc/database_lightning.png</iconset>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
 | 
					  <layout class="QGridLayout" name="gridLayout_2">
 | 
				
			||||||
 | 
					   <property name="leftMargin">
 | 
				
			||||||
 | 
					    <number>0</number>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="topMargin">
 | 
				
			||||||
 | 
					    <number>0</number>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="rightMargin">
 | 
				
			||||||
 | 
					    <number>0</number>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="bottomMargin">
 | 
				
			||||||
 | 
					    <number>0</number>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="verticalSpacing">
 | 
				
			||||||
 | 
					    <number>0</number>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <item row="0" column="0">
 | 
				
			||||||
 | 
					    <widget class="QPlainTextEdit" name="contentArea">
 | 
				
			||||||
 | 
					     <property name="frameShape">
 | 
				
			||||||
 | 
					      <enum>QFrame::NoFrame</enum>
 | 
				
			||||||
 | 
					     </property>
 | 
				
			||||||
 | 
					    </widget>
 | 
				
			||||||
 | 
					   </item>
 | 
				
			||||||
 | 
					   <item row="1" column="0">
 | 
				
			||||||
 | 
					    <widget class="QFrame" name="frame">
 | 
				
			||||||
 | 
					     <property name="frameShape">
 | 
				
			||||||
 | 
					      <enum>QFrame::StyledPanel</enum>
 | 
				
			||||||
 | 
					     </property>
 | 
				
			||||||
 | 
					     <property name="frameShadow">
 | 
				
			||||||
 | 
					      <enum>QFrame::Raised</enum>
 | 
				
			||||||
 | 
					     </property>
 | 
				
			||||||
 | 
					     <layout class="QGridLayout" name="gridLayout">
 | 
				
			||||||
 | 
					      <item row="0" column="0">
 | 
				
			||||||
 | 
					       <widget class="QDialogButtonBox" name="buttonBox">
 | 
				
			||||||
 | 
					        <property name="standardButtons">
 | 
				
			||||||
 | 
					         <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
 | 
				
			||||||
 | 
					        </property>
 | 
				
			||||||
 | 
					       </widget>
 | 
				
			||||||
 | 
					      </item>
 | 
				
			||||||
 | 
					     </layout>
 | 
				
			||||||
 | 
					    </widget>
 | 
				
			||||||
 | 
					   </item>
 | 
				
			||||||
 | 
					  </layout>
 | 
				
			||||||
 | 
					 </widget>
 | 
				
			||||||
 | 
					 <resources>
 | 
				
			||||||
 | 
					  <include location="resources.qrc"/>
 | 
				
			||||||
 | 
					 </resources>
 | 
				
			||||||
 | 
					 <connections>
 | 
				
			||||||
 | 
					  <connection>
 | 
				
			||||||
 | 
					   <sender>buttonBox</sender>
 | 
				
			||||||
 | 
					   <signal>rejected()</signal>
 | 
				
			||||||
 | 
					   <receiver>ItemWindow</receiver>
 | 
				
			||||||
 | 
					   <slot>reject()</slot>
 | 
				
			||||||
 | 
					   <hints>
 | 
				
			||||||
 | 
					    <hint type="sourcelabel">
 | 
				
			||||||
 | 
					     <x>184</x>
 | 
				
			||||||
 | 
					     <y>330</y>
 | 
				
			||||||
 | 
					    </hint>
 | 
				
			||||||
 | 
					    <hint type="destinationlabel">
 | 
				
			||||||
 | 
					     <x>184</x>
 | 
				
			||||||
 | 
					     <y>176</y>
 | 
				
			||||||
 | 
					    </hint>
 | 
				
			||||||
 | 
					   </hints>
 | 
				
			||||||
 | 
					  </connection>
 | 
				
			||||||
 | 
					  <connection>
 | 
				
			||||||
 | 
					   <sender>buttonBox</sender>
 | 
				
			||||||
 | 
					   <signal>accepted()</signal>
 | 
				
			||||||
 | 
					   <receiver>ItemWindow</receiver>
 | 
				
			||||||
 | 
					   <slot>accept()</slot>
 | 
				
			||||||
 | 
					   <hints>
 | 
				
			||||||
 | 
					    <hint type="sourcelabel">
 | 
				
			||||||
 | 
					     <x>184</x>
 | 
				
			||||||
 | 
					     <y>330</y>
 | 
				
			||||||
 | 
					    </hint>
 | 
				
			||||||
 | 
					    <hint type="destinationlabel">
 | 
				
			||||||
 | 
					     <x>184</x>
 | 
				
			||||||
 | 
					     <y>176</y>
 | 
				
			||||||
 | 
					    </hint>
 | 
				
			||||||
 | 
					   </hints>
 | 
				
			||||||
 | 
					  </connection>
 | 
				
			||||||
 | 
					 </connections>
 | 
				
			||||||
 | 
					</ui>
 | 
				
			||||||
							
								
								
									
										23
									
								
								qbolt/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					#include "mainwindow.h"
 | 
				
			||||||
 | 
					#include <QApplication>
 | 
				
			||||||
 | 
					#include "interop.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QDebug>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int64_t magic = Interop::GetMagic();
 | 
				
			||||||
 | 
					    if (magic != 0x10203040) {
 | 
				
			||||||
 | 
					        qDebug() << "bad magic " << magic;
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QApplication a(argc, argv);
 | 
				
			||||||
 | 
					    QApplication::setApplicationDisplayName("QBolt");
 | 
				
			||||||
 | 
					    QApplication::setWindowIcon(QIcon(":/rsrc/database_lightning.png"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MainWindow w;
 | 
				
			||||||
 | 
					    w.show();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return a.exec();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										478
									
								
								qbolt/mainwindow.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,478 @@
 | 
				
			|||||||
 | 
					#include "mainwindow.h"
 | 
				
			||||||
 | 
					#include "ui_mainwindow.h"
 | 
				
			||||||
 | 
					#include "itemwindow.h"
 | 
				
			||||||
 | 
					#include "boltdb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QFileDialog>
 | 
				
			||||||
 | 
					#include <QMessageBox>
 | 
				
			||||||
 | 
					#include <QJsonDocument>
 | 
				
			||||||
 | 
					#include <QInputDialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MainWindow::MainWindow(QWidget *parent) :
 | 
				
			||||||
 | 
					    QMainWindow(parent),
 | 
				
			||||||
 | 
					    ui(new Ui::MainWindow)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ui->setupUi(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    on_bucketTree_currentItemChanged(nullptr, nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    databaseContext = new QMenu();
 | 
				
			||||||
 | 
					    databaseContext->addAction(ui->actionRefresh_buckets);
 | 
				
			||||||
 | 
					    databaseContext->addAction(ui->actionAdd_bucket);
 | 
				
			||||||
 | 
					    databaseContext->addSeparator();
 | 
				
			||||||
 | 
					    databaseContext->addAction(ui->actionDisconnect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bucketContext = new QMenu();
 | 
				
			||||||
 | 
					    bucketContext->addAction(ui->actionRefresh_buckets);
 | 
				
			||||||
 | 
					    bucketContext->addAction(ui->actionAdd_bucket);
 | 
				
			||||||
 | 
					    bucketContext->addSeparator();
 | 
				
			||||||
 | 
					    bucketContext->addAction(ui->actionDelete_bucket);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MainWindow::~MainWindow()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    delete ui;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 GET_BDB(top) static_cast<BoltDB*>( top->data(0, BdbPointerRole).value<void*>() )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionNew_database_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QString file = QFileDialog::getSaveFileName(this, tr("Save new bolt database as..."));
 | 
				
			||||||
 | 
					    if (file.length()) {
 | 
				
			||||||
 | 
					        openDatabase(file, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionOpen_database_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QString file = QFileDialog::getOpenFileName(this, tr("Select bolt database..."));
 | 
				
			||||||
 | 
					    if (file.length()) {
 | 
				
			||||||
 | 
					        openDatabase(file, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					    QString error;
 | 
				
			||||||
 | 
					    auto *bdb = BoltDB::createFrom(file, readOnly, error);
 | 
				
			||||||
 | 
					    if (bdb == nullptr) {
 | 
				
			||||||
 | 
					        QMessageBox qmb;
 | 
				
			||||||
 | 
					        qmb.setText(tr("Error opening database: %1").arg(error));
 | 
				
			||||||
 | 
					        qmb.exec();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QTreeWidgetItem *top = new QTreeWidgetItem();
 | 
				
			||||||
 | 
					    top->setText(0, QFileInfo(file).fileName());
 | 
				
			||||||
 | 
					    top->setIcon(0, QIcon(":/rsrc/database.png"));
 | 
				
			||||||
 | 
					    SET_BDB(top, bdb);
 | 
				
			||||||
 | 
					    ui->bucketTree->addTopLevelItem(top);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    refreshBucketTree(top);
 | 
				
			||||||
 | 
					    ui->bucketTree->setCurrentItem(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)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QTreeWidgetItem *top = itm;
 | 
				
			||||||
 | 
					    QList<QByteArray> browsePath;
 | 
				
			||||||
 | 
					    while(top->parent() != nullptr) {
 | 
				
			||||||
 | 
					        browsePath.push_front(top->data(0, BinaryDataRole).toByteArray());
 | 
				
			||||||
 | 
					        top = top->parent();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Remove existing children
 | 
				
			||||||
 | 
					    for (int i = itm->childCount(); i --> 0;) {
 | 
				
			||||||
 | 
					        delete itm->takeChild(i);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto *bdb = GET_BDB(top);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString error;
 | 
				
			||||||
 | 
					    bool ok = bdb->listBuckets(
 | 
				
			||||||
 | 
					        browsePath,
 | 
				
			||||||
 | 
					        error,
 | 
				
			||||||
 | 
					        [=](QByteArray qba){
 | 
				
			||||||
 | 
					            QTreeWidgetItem *child = new QTreeWidgetItem();
 | 
				
			||||||
 | 
					            child->setText(0, getDisplayName(qba));
 | 
				
			||||||
 | 
					            child->setData(0, BinaryDataRole, qba);
 | 
				
			||||||
 | 
					            child->setIcon(0, QIcon(":/rsrc/table.png"));
 | 
				
			||||||
 | 
					            itm->addChild(child);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            refreshBucketTree(child);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!ok) {
 | 
				
			||||||
 | 
					        QMessageBox qmb;
 | 
				
			||||||
 | 
					        qmb.setText(tr("Error listing buckets: %1").arg(error));
 | 
				
			||||||
 | 
					        qmb.exec();
 | 
				
			||||||
 | 
					        // (continue)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionExit_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    close();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionAbout_Qt_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QApplication::aboutQt();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionAbout_qbolt_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QMessageBox::about(
 | 
				
			||||||
 | 
					        this,
 | 
				
			||||||
 | 
					        QApplication::applicationDisplayName(),
 | 
				
			||||||
 | 
					        "<b>QBolt</b><br>Graphical interface for managing Bolt databases<br><br>"
 | 
				
			||||||
 | 
					        "- <a href='https://github.com/boltdb/bolt'>About BoltDB</a><br>"
 | 
				
			||||||
 | 
					        "- <a href='http://www.famfamfam.com/lab/icons/silk/'>FamFamFam "Silk" icon set</a><br>"
 | 
				
			||||||
 | 
					        "- <a href='https://code.ivysaur.me/qbolt'>QBolt homepage</a><br>"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionDisconnect_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QTreeWidgetItem *top = lastContextSelection;
 | 
				
			||||||
 | 
					    if (top->parent()) {
 | 
				
			||||||
 | 
					        return; // somehow we didn't select a top-level item
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto *bdb = GET_BDB(top);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Remove UI
 | 
				
			||||||
 | 
					    ui->bucketTree->clearSelection();
 | 
				
			||||||
 | 
					    delete top;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Disconnect from DB
 | 
				
			||||||
 | 
					    delete bdb;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_bucketTree_customContextMenuRequested(const QPoint &pos)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto *itm = ui->bucketTree->itemAt(pos);
 | 
				
			||||||
 | 
					    if (itm == nullptr) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    lastContextSelection = itm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (itm->parent() != nullptr) {
 | 
				
			||||||
 | 
					        // Child item, show the bucket menu
 | 
				
			||||||
 | 
					        bucketContext->popup(ui->bucketTree->mapToGlobal(pos));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // Top-level item, show the database menu
 | 
				
			||||||
 | 
					        databaseContext->popup(ui->bucketTree->mapToGlobal(pos));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionRefresh_buckets_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    refreshBucketTree(lastContextSelection);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_bucketTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Q_UNUSED(previous);
 | 
				
			||||||
 | 
					    if (current == nullptr) {
 | 
				
			||||||
 | 
					        ui->stackedWidget->setVisible(false);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui->stackedWidget->setVisible(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (current->parent() == nullptr) {
 | 
				
			||||||
 | 
					        // Selected a database
 | 
				
			||||||
 | 
					        ui->stackedWidget->setCurrentWidget(ui->databasePage);
 | 
				
			||||||
 | 
					        ui->databasePropertiesArea->clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto *bdb = GET_BDB(current);
 | 
				
			||||||
 | 
					        bdb->getStatsJSON(
 | 
				
			||||||
 | 
					            [=](QByteArray j) {
 | 
				
			||||||
 | 
					                auto doc = QJsonDocument::fromJson(j);
 | 
				
			||||||
 | 
					                ui->databasePropertiesArea->setPlainText(QString::fromUtf8(doc.toJson(QJsonDocument::Indented)));
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            [=](QString error) {
 | 
				
			||||||
 | 
					                ui->databasePropertiesArea->setPlainText(tr("Error retrieving database statistics: %1").arg(error));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Clean up foreign areas
 | 
				
			||||||
 | 
					        ui->bucketPropertiesArea->clear();
 | 
				
			||||||
 | 
					        ui->bucketData->clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // Selected a bucket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ui->stackedWidget->setCurrentWidget(ui->bucketPage);
 | 
				
			||||||
 | 
					        ui->bucketPropertiesArea->clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        QList<QByteArray> browse;
 | 
				
			||||||
 | 
					        QTreeWidgetItem *top = current;
 | 
				
			||||||
 | 
					        while (top->parent() != nullptr) {
 | 
				
			||||||
 | 
					            browse.push_front(top->data(0, BinaryDataRole).toByteArray());
 | 
				
			||||||
 | 
					            top = top->parent();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        auto *bdb = GET_BDB(top);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bdb->getBucketStatsJSON(
 | 
				
			||||||
 | 
					            browse,
 | 
				
			||||||
 | 
					            [=](QByteArray j) {
 | 
				
			||||||
 | 
					                auto doc = QJsonDocument::fromJson(j);
 | 
				
			||||||
 | 
					                ui->bucketPropertiesArea->setPlainText(QString::fromUtf8(doc.toJson(QJsonDocument::Indented)));
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            [=](QString error) {
 | 
				
			||||||
 | 
					                ui->bucketPropertiesArea->setPlainText(tr("Error retrieving bucket statistics: %1").arg(error));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Load the data tab
 | 
				
			||||||
 | 
					        refreshData(bdb, browse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Clean up foreign areas
 | 
				
			||||||
 | 
					        ui->databasePropertiesArea->clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::refreshData(BoltDB *bdb, const QList<QByteArray>& browse)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // Load the data tab
 | 
				
			||||||
 | 
					    ui->bucketData->clear();
 | 
				
			||||||
 | 
					    QString err;
 | 
				
			||||||
 | 
					    bool ok = bdb->listKeys(browse, err, [=](QByteArray name, int64_t dataLen) {
 | 
				
			||||||
 | 
					        auto *itm = new QTreeWidgetItem();
 | 
				
			||||||
 | 
					        itm->setText(0, getDisplayName(name));
 | 
				
			||||||
 | 
					        itm->setData(0, BinaryDataRole, name);
 | 
				
			||||||
 | 
					        itm->setText(1, QString("%1").arg(dataLen));
 | 
				
			||||||
 | 
					        ui->bucketData->addTopLevelItem(itm);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (! ok) {
 | 
				
			||||||
 | 
					        QMessageBox qmb;
 | 
				
			||||||
 | 
					        qmb.setText(tr("Error listing bucket content: %1").arg(err));
 | 
				
			||||||
 | 
					        qmb.exec();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui->bucketData->resizeColumnToContents(0);
 | 
				
			||||||
 | 
					    on_bucketData_itemSelectionChanged();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionClear_selection_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ui->bucketTree->setCurrentItem(nullptr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GET_ITM_TOP_BROWSE_BDB \
 | 
				
			||||||
 | 
					    QTreeWidgetItem* itm = ui->bucketTree->currentItem(); \
 | 
				
			||||||
 | 
					    if (itm == nullptr) { \
 | 
				
			||||||
 | 
					        return; \
 | 
				
			||||||
 | 
					    } \
 | 
				
			||||||
 | 
					    QTreeWidgetItem* top = itm; \
 | 
				
			||||||
 | 
					    QList<QByteArray> browse; \
 | 
				
			||||||
 | 
					    while(top->parent() != nullptr) { \
 | 
				
			||||||
 | 
					        browse.push_front(top->data(0, BinaryDataRole).toByteArray()); \
 | 
				
			||||||
 | 
					        top = top->parent(); \
 | 
				
			||||||
 | 
					    } \
 | 
				
			||||||
 | 
					    auto *bdb = GET_BDB(top);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::openEditor(BoltDB *bdb, const QList<QByteArray>& saveAs, QByteArray saveAsKey, QByteArray currentContent)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto iw = new ItemWindow();
 | 
				
			||||||
 | 
					    iw->ContentArea()->setPlainText(QString::fromUtf8(currentContent));
 | 
				
			||||||
 | 
					    iw->setWindowTitle(QString::fromUtf8(saveAsKey));
 | 
				
			||||||
 | 
					    iw->setWindowModality(Qt::ApplicationModal); // we need this - otherwise we'll refresh a possibly-changed area after saving
 | 
				
			||||||
 | 
					    connect(iw, &ItemWindow::finished, iw, [=](int exitCode){
 | 
				
			||||||
 | 
					        if (exitCode == ItemWindow::Accepted) {
 | 
				
			||||||
 | 
					            QString err;
 | 
				
			||||||
 | 
					            if (! bdb->setItem(saveAs, saveAsKey, iw->ContentArea()->toPlainText().toUtf8(), err)) {
 | 
				
			||||||
 | 
					                QMessageBox qmb;
 | 
				
			||||||
 | 
					                qmb.setText(tr("Error saving item content: %1").arg(err));
 | 
				
			||||||
 | 
					                qmb.exec();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            refreshData(bdb, saveAs);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        iw->deleteLater();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    iw->show();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_bucketData_doubleClicked(const QModelIndex &index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GET_ITM_TOP_BROWSE_BDB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Get item key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto model = index.model();
 | 
				
			||||||
 | 
					    const QByteArray& key = model->data(model->index(index.row(), 0), BinaryDataRole).toByteArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // DB lookup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bdb->getData(
 | 
				
			||||||
 | 
					        browse,
 | 
				
			||||||
 | 
					        key,
 | 
				
			||||||
 | 
					        [=](QByteArray content) {
 | 
				
			||||||
 | 
					            openEditor(bdb, browse, key, content);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        [=](QString error) {
 | 
				
			||||||
 | 
					            QMessageBox qmb;
 | 
				
			||||||
 | 
					            qmb.setText(tr("Error loading item content: %1").arg(error));
 | 
				
			||||||
 | 
					            qmb.exec();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionAdd_bucket_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GET_ITM_TOP_BROWSE_BDB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Prompt for bucket name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString name = QInputDialog::getText(this, tr("New bucket"), tr("Enter a key for the new bucket:"));
 | 
				
			||||||
 | 
					    if (name.length() == 0) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create
 | 
				
			||||||
 | 
					    QString err;
 | 
				
			||||||
 | 
					    if (! bdb->addBucket(browse, name.toUtf8(), err)) {
 | 
				
			||||||
 | 
					        QMessageBox qmb;
 | 
				
			||||||
 | 
					        qmb.setText(tr("Error creating bucket: %1").arg(err));
 | 
				
			||||||
 | 
					        qmb.exec();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Refresh bucket list
 | 
				
			||||||
 | 
					    refreshBucketTree(itm); // sub-tree only
 | 
				
			||||||
 | 
					    ui->bucketTree->expandItem(itm);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_actionDelete_bucket_triggered()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GET_ITM_TOP_BROWSE_BDB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Prompt for confirmation
 | 
				
			||||||
 | 
					    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(getDisplayName(bucketToDelete)),
 | 
				
			||||||
 | 
					            QMessageBox::Yes,
 | 
				
			||||||
 | 
					            QMessageBox::Cancel
 | 
				
			||||||
 | 
					        ) != QMessageBox::Yes
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QTreeWidgetItem* parent = itm->parent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // One level down
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    browse.pop_back();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString err;
 | 
				
			||||||
 | 
					    if (! bdb->deleteBucket(browse, bucketToDelete, err)) {
 | 
				
			||||||
 | 
					        QMessageBox qmb;
 | 
				
			||||||
 | 
					        qmb.setText(tr("Error removing bucket: %1").arg(err));
 | 
				
			||||||
 | 
					        qmb.exec();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Refresh bucket list
 | 
				
			||||||
 | 
					    refreshBucketTree(parent); // sub-tree only
 | 
				
			||||||
 | 
					    ui->bucketTree->expandItem(parent);
 | 
				
			||||||
 | 
					    ui->bucketTree->setCurrentItem(parent);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_AddDataButton_clicked()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GET_ITM_TOP_BROWSE_BDB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Prompt for bucket name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString name = QInputDialog::getText(this, tr("New item"), tr("Enter a key for the new item:"));
 | 
				
			||||||
 | 
					    if (name.length() == 0) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    openEditor(bdb, browse, name.toUtf8(), QByteArray());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_DeleteDataButton_clicked()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GET_ITM_TOP_BROWSE_BDB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto selection = ui->bucketData->selectedItems();
 | 
				
			||||||
 | 
					    if (selection.length() == 0) {
 | 
				
			||||||
 | 
					        return; // nothing to do
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Prompt for confirmation
 | 
				
			||||||
 | 
					    if (QMessageBox::question(this, tr("Delete items"), tr("Are you sure you want to remove %1 item(s)?").arg(selection.length()), QMessageBox::Yes, QMessageBox::Cancel) != QMessageBox::Yes) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString err;
 | 
				
			||||||
 | 
					    for (int i = selection.length(); i-->0;) {
 | 
				
			||||||
 | 
					        if (! bdb->deleteItem(browse, selection[i]->data(0, BinaryDataRole).toByteArray(), err)) {
 | 
				
			||||||
 | 
					            QMessageBox qmb;
 | 
				
			||||||
 | 
					            qmb.setText(tr("Error removing item: %1").arg(err));
 | 
				
			||||||
 | 
					            qmb.exec();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    refreshData(bdb, browse);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_bucketData_itemSelectionChanged()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ui->DeleteDataButton->setEnabled( (ui->bucketData->selectedItems().size() > 0) );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										70
									
								
								qbolt/mainwindow.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					#ifndef MAINWINDOW_H
 | 
				
			||||||
 | 
					#define MAINWINDOW_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "boltdb.h"
 | 
				
			||||||
 | 
					#include <QMainWindow>
 | 
				
			||||||
 | 
					#include <QMenu>
 | 
				
			||||||
 | 
					#include <QTreeWidgetItem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ui {
 | 
				
			||||||
 | 
					class MainWindow;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MainWindow : public QMainWindow
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit MainWindow(QWidget *parent = 0);
 | 
				
			||||||
 | 
					    ~MainWindow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
					    void on_actionOpen_database_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionExit_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionAbout_Qt_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionAbout_qbolt_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionDisconnect_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_bucketTree_customContextMenuRequested(const QPoint &pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionRefresh_buckets_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_bucketTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionClear_selection_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_bucketData_doubleClicked(const QModelIndex &index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionNew_database_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionAdd_bucket_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionDelete_bucket_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_AddDataButton_clicked();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_DeleteDataButton_clicked();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_bucketData_itemSelectionChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void on_actionOpen_database_as_read_only_triggered();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    void openDatabase(QString file, bool readOnly);
 | 
				
			||||||
 | 
					    void refreshBucketTree(QTreeWidgetItem* top);
 | 
				
			||||||
 | 
					    void refreshData(BoltDB *bdb, const QList<QByteArray>& browse);
 | 
				
			||||||
 | 
					    void openEditor(BoltDB *bdb, const QList<QByteArray>& saveAs, QByteArray saveAsKey, QByteArray currentContent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    Ui::MainWindow *ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QMenu *databaseContext;
 | 
				
			||||||
 | 
					    QMenu *bucketContext;
 | 
				
			||||||
 | 
					    QTreeWidgetItem* lastContextSelection;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // MAINWINDOW_H
 | 
				
			||||||
							
								
								
									
										410
									
								
								qbolt/mainwindow.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,410 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<ui version="4.0">
 | 
				
			||||||
 | 
					 <class>MainWindow</class>
 | 
				
			||||||
 | 
					 <widget class="QMainWindow" name="MainWindow">
 | 
				
			||||||
 | 
					  <property name="geometry">
 | 
				
			||||||
 | 
					   <rect>
 | 
				
			||||||
 | 
					    <x>0</x>
 | 
				
			||||||
 | 
					    <y>0</y>
 | 
				
			||||||
 | 
					    <width>668</width>
 | 
				
			||||||
 | 
					    <height>405</height>
 | 
				
			||||||
 | 
					   </rect>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
 | 
					  <property name="windowTitle">
 | 
				
			||||||
 | 
					   <string>QBolt</string>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
 | 
					  <property name="windowIcon">
 | 
				
			||||||
 | 
					   <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					    <normaloff>:/rsrc/database_lightning.png</normaloff>:/rsrc/database_lightning.png</iconset>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
 | 
					  <widget class="QWidget" name="centralWidget">
 | 
				
			||||||
 | 
					   <layout class="QGridLayout" name="gridLayout">
 | 
				
			||||||
 | 
					    <property name="leftMargin">
 | 
				
			||||||
 | 
					     <number>0</number>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="topMargin">
 | 
				
			||||||
 | 
					     <number>0</number>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="rightMargin">
 | 
				
			||||||
 | 
					     <number>0</number>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="bottomMargin">
 | 
				
			||||||
 | 
					     <number>0</number>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <item row="0" column="0">
 | 
				
			||||||
 | 
					     <widget class="QSplitter" name="splitter">
 | 
				
			||||||
 | 
					      <property name="orientation">
 | 
				
			||||||
 | 
					       <enum>Qt::Horizontal</enum>
 | 
				
			||||||
 | 
					      </property>
 | 
				
			||||||
 | 
					      <property name="childrenCollapsible">
 | 
				
			||||||
 | 
					       <bool>false</bool>
 | 
				
			||||||
 | 
					      </property>
 | 
				
			||||||
 | 
					      <widget class="QTreeWidget" name="bucketTree">
 | 
				
			||||||
 | 
					       <property name="contextMenuPolicy">
 | 
				
			||||||
 | 
					        <enum>Qt::CustomContextMenu</enum>
 | 
				
			||||||
 | 
					       </property>
 | 
				
			||||||
 | 
					       <property name="uniformRowHeights">
 | 
				
			||||||
 | 
					        <bool>true</bool>
 | 
				
			||||||
 | 
					       </property>
 | 
				
			||||||
 | 
					       <column>
 | 
				
			||||||
 | 
					        <property name="text">
 | 
				
			||||||
 | 
					         <string>Bucket</string>
 | 
				
			||||||
 | 
					        </property>
 | 
				
			||||||
 | 
					       </column>
 | 
				
			||||||
 | 
					      </widget>
 | 
				
			||||||
 | 
					      <widget class="QStackedWidget" name="stackedWidget">
 | 
				
			||||||
 | 
					       <property name="currentIndex">
 | 
				
			||||||
 | 
					        <number>0</number>
 | 
				
			||||||
 | 
					       </property>
 | 
				
			||||||
 | 
					       <widget class="QWidget" name="databasePage">
 | 
				
			||||||
 | 
					        <layout class="QGridLayout" name="gridLayout_4">
 | 
				
			||||||
 | 
					         <property name="leftMargin">
 | 
				
			||||||
 | 
					          <number>0</number>
 | 
				
			||||||
 | 
					         </property>
 | 
				
			||||||
 | 
					         <property name="topMargin">
 | 
				
			||||||
 | 
					          <number>0</number>
 | 
				
			||||||
 | 
					         </property>
 | 
				
			||||||
 | 
					         <property name="rightMargin">
 | 
				
			||||||
 | 
					          <number>0</number>
 | 
				
			||||||
 | 
					         </property>
 | 
				
			||||||
 | 
					         <property name="bottomMargin">
 | 
				
			||||||
 | 
					          <number>0</number>
 | 
				
			||||||
 | 
					         </property>
 | 
				
			||||||
 | 
					         <item row="0" column="0">
 | 
				
			||||||
 | 
					          <widget class="QTabWidget" name="databaseTabWidget">
 | 
				
			||||||
 | 
					           <property name="currentIndex">
 | 
				
			||||||
 | 
					            <number>0</number>
 | 
				
			||||||
 | 
					           </property>
 | 
				
			||||||
 | 
					           <widget class="QWidget" name="databasePropertiesTab">
 | 
				
			||||||
 | 
					            <attribute name="icon">
 | 
				
			||||||
 | 
					             <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					              <normaloff>:/rsrc/chart_bar.png</normaloff>:/rsrc/chart_bar.png</iconset>
 | 
				
			||||||
 | 
					            </attribute>
 | 
				
			||||||
 | 
					            <attribute name="title">
 | 
				
			||||||
 | 
					             <string>Database</string>
 | 
				
			||||||
 | 
					            </attribute>
 | 
				
			||||||
 | 
					            <layout class="QGridLayout" name="gridLayout_2">
 | 
				
			||||||
 | 
					             <property name="leftMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="topMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="rightMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="bottomMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <item row="0" column="0">
 | 
				
			||||||
 | 
					              <widget class="QPlainTextEdit" name="databasePropertiesArea">
 | 
				
			||||||
 | 
					               <property name="frameShape">
 | 
				
			||||||
 | 
					                <enum>QFrame::NoFrame</enum>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="readOnly">
 | 
				
			||||||
 | 
					                <bool>true</bool>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="plainText">
 | 
				
			||||||
 | 
					                <string>No selection</string>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					              </widget>
 | 
				
			||||||
 | 
					             </item>
 | 
				
			||||||
 | 
					            </layout>
 | 
				
			||||||
 | 
					           </widget>
 | 
				
			||||||
 | 
					          </widget>
 | 
				
			||||||
 | 
					         </item>
 | 
				
			||||||
 | 
					        </layout>
 | 
				
			||||||
 | 
					       </widget>
 | 
				
			||||||
 | 
					       <widget class="QWidget" name="bucketPage">
 | 
				
			||||||
 | 
					        <layout class="QGridLayout" name="gridLayout_3">
 | 
				
			||||||
 | 
					         <property name="leftMargin">
 | 
				
			||||||
 | 
					          <number>0</number>
 | 
				
			||||||
 | 
					         </property>
 | 
				
			||||||
 | 
					         <property name="topMargin">
 | 
				
			||||||
 | 
					          <number>0</number>
 | 
				
			||||||
 | 
					         </property>
 | 
				
			||||||
 | 
					         <property name="rightMargin">
 | 
				
			||||||
 | 
					          <number>0</number>
 | 
				
			||||||
 | 
					         </property>
 | 
				
			||||||
 | 
					         <property name="bottomMargin">
 | 
				
			||||||
 | 
					          <number>0</number>
 | 
				
			||||||
 | 
					         </property>
 | 
				
			||||||
 | 
					         <item row="0" column="0">
 | 
				
			||||||
 | 
					          <widget class="QTabWidget" name="bucketTabWidget">
 | 
				
			||||||
 | 
					           <property name="currentIndex">
 | 
				
			||||||
 | 
					            <number>0</number>
 | 
				
			||||||
 | 
					           </property>
 | 
				
			||||||
 | 
					           <widget class="QWidget" name="bucketPropertiesTab">
 | 
				
			||||||
 | 
					            <attribute name="icon">
 | 
				
			||||||
 | 
					             <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					              <normaloff>:/rsrc/chart_bar.png</normaloff>:/rsrc/chart_bar.png</iconset>
 | 
				
			||||||
 | 
					            </attribute>
 | 
				
			||||||
 | 
					            <attribute name="title">
 | 
				
			||||||
 | 
					             <string>Bucket</string>
 | 
				
			||||||
 | 
					            </attribute>
 | 
				
			||||||
 | 
					            <layout class="QGridLayout" name="gridLayout_5">
 | 
				
			||||||
 | 
					             <property name="leftMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="topMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="rightMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="bottomMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <item row="0" column="0">
 | 
				
			||||||
 | 
					              <widget class="QPlainTextEdit" name="bucketPropertiesArea">
 | 
				
			||||||
 | 
					               <property name="frameShape">
 | 
				
			||||||
 | 
					                <enum>QFrame::NoFrame</enum>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="readOnly">
 | 
				
			||||||
 | 
					                <bool>true</bool>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					              </widget>
 | 
				
			||||||
 | 
					             </item>
 | 
				
			||||||
 | 
					            </layout>
 | 
				
			||||||
 | 
					           </widget>
 | 
				
			||||||
 | 
					           <widget class="QWidget" name="bucketDataTab">
 | 
				
			||||||
 | 
					            <attribute name="icon">
 | 
				
			||||||
 | 
					             <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					              <normaloff>:/rsrc/table.png</normaloff>:/rsrc/table.png</iconset>
 | 
				
			||||||
 | 
					            </attribute>
 | 
				
			||||||
 | 
					            <attribute name="title">
 | 
				
			||||||
 | 
					             <string>Data</string>
 | 
				
			||||||
 | 
					            </attribute>
 | 
				
			||||||
 | 
					            <layout class="QGridLayout" name="gridLayout_6">
 | 
				
			||||||
 | 
					             <property name="leftMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="topMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="rightMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <property name="bottomMargin">
 | 
				
			||||||
 | 
					              <number>3</number>
 | 
				
			||||||
 | 
					             </property>
 | 
				
			||||||
 | 
					             <item row="0" column="0" colspan="3">
 | 
				
			||||||
 | 
					              <widget class="QTreeWidget" name="bucketData">
 | 
				
			||||||
 | 
					               <property name="selectionMode">
 | 
				
			||||||
 | 
					                <enum>QAbstractItemView::ExtendedSelection</enum>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="indentation">
 | 
				
			||||||
 | 
					                <number>0</number>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="rootIsDecorated">
 | 
				
			||||||
 | 
					                <bool>false</bool>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="uniformRowHeights">
 | 
				
			||||||
 | 
					                <bool>true</bool>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="itemsExpandable">
 | 
				
			||||||
 | 
					                <bool>false</bool>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <column>
 | 
				
			||||||
 | 
					                <property name="text">
 | 
				
			||||||
 | 
					                 <string>Key</string>
 | 
				
			||||||
 | 
					                </property>
 | 
				
			||||||
 | 
					               </column>
 | 
				
			||||||
 | 
					               <column>
 | 
				
			||||||
 | 
					                <property name="text">
 | 
				
			||||||
 | 
					                 <string>Data length</string>
 | 
				
			||||||
 | 
					                </property>
 | 
				
			||||||
 | 
					               </column>
 | 
				
			||||||
 | 
					              </widget>
 | 
				
			||||||
 | 
					             </item>
 | 
				
			||||||
 | 
					             <item row="1" column="0">
 | 
				
			||||||
 | 
					              <widget class="QPushButton" name="AddDataButton">
 | 
				
			||||||
 | 
					               <property name="text">
 | 
				
			||||||
 | 
					                <string>Add...</string>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="icon">
 | 
				
			||||||
 | 
					                <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					                 <normaloff>:/rsrc/add.png</normaloff>:/rsrc/add.png</iconset>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					              </widget>
 | 
				
			||||||
 | 
					             </item>
 | 
				
			||||||
 | 
					             <item row="1" column="2">
 | 
				
			||||||
 | 
					              <spacer name="horizontalSpacer">
 | 
				
			||||||
 | 
					               <property name="orientation">
 | 
				
			||||||
 | 
					                <enum>Qt::Horizontal</enum>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="sizeHint" stdset="0">
 | 
				
			||||||
 | 
					                <size>
 | 
				
			||||||
 | 
					                 <width>40</width>
 | 
				
			||||||
 | 
					                 <height>20</height>
 | 
				
			||||||
 | 
					                </size>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					              </spacer>
 | 
				
			||||||
 | 
					             </item>
 | 
				
			||||||
 | 
					             <item row="1" column="1">
 | 
				
			||||||
 | 
					              <widget class="QPushButton" name="DeleteDataButton">
 | 
				
			||||||
 | 
					               <property name="text">
 | 
				
			||||||
 | 
					                <string>Delete...</string>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					               <property name="icon">
 | 
				
			||||||
 | 
					                <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					                 <normaloff>:/rsrc/delete.png</normaloff>:/rsrc/delete.png</iconset>
 | 
				
			||||||
 | 
					               </property>
 | 
				
			||||||
 | 
					              </widget>
 | 
				
			||||||
 | 
					             </item>
 | 
				
			||||||
 | 
					            </layout>
 | 
				
			||||||
 | 
					           </widget>
 | 
				
			||||||
 | 
					          </widget>
 | 
				
			||||||
 | 
					         </item>
 | 
				
			||||||
 | 
					        </layout>
 | 
				
			||||||
 | 
					       </widget>
 | 
				
			||||||
 | 
					      </widget>
 | 
				
			||||||
 | 
					     </widget>
 | 
				
			||||||
 | 
					    </item>
 | 
				
			||||||
 | 
					   </layout>
 | 
				
			||||||
 | 
					  </widget>
 | 
				
			||||||
 | 
					  <widget class="QMenuBar" name="menuBar">
 | 
				
			||||||
 | 
					   <property name="geometry">
 | 
				
			||||||
 | 
					    <rect>
 | 
				
			||||||
 | 
					     <x>0</x>
 | 
				
			||||||
 | 
					     <y>0</y>
 | 
				
			||||||
 | 
					     <width>668</width>
 | 
				
			||||||
 | 
					     <height>21</height>
 | 
				
			||||||
 | 
					    </rect>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <widget class="QMenu" name="menuFile">
 | 
				
			||||||
 | 
					    <property name="title">
 | 
				
			||||||
 | 
					     <string>Fi&le</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <addaction name="actionNew_database"/>
 | 
				
			||||||
 | 
					    <addaction name="actionOpen_database"/>
 | 
				
			||||||
 | 
					    <addaction name="actionOpen_database_as_read_only"/>
 | 
				
			||||||
 | 
					    <addaction name="separator"/>
 | 
				
			||||||
 | 
					    <addaction name="actionExit"/>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QMenu" name="menuHelp">
 | 
				
			||||||
 | 
					    <property name="title">
 | 
				
			||||||
 | 
					     <string>Help</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <addaction name="actionAbout_qbolt"/>
 | 
				
			||||||
 | 
					    <addaction name="actionAbout_Qt"/>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QMenu" name="menuView">
 | 
				
			||||||
 | 
					    <property name="title">
 | 
				
			||||||
 | 
					     <string>&View</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <addaction name="actionClear_selection"/>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <addaction name="menuFile"/>
 | 
				
			||||||
 | 
					   <addaction name="menuView"/>
 | 
				
			||||||
 | 
					   <addaction name="menuHelp"/>
 | 
				
			||||||
 | 
					  </widget>
 | 
				
			||||||
 | 
					  <widget class="QToolBar" name="mainToolBar">
 | 
				
			||||||
 | 
					   <attribute name="toolBarArea">
 | 
				
			||||||
 | 
					    <enum>TopToolBarArea</enum>
 | 
				
			||||||
 | 
					   </attribute>
 | 
				
			||||||
 | 
					   <attribute name="toolBarBreak">
 | 
				
			||||||
 | 
					    <bool>false</bool>
 | 
				
			||||||
 | 
					   </attribute>
 | 
				
			||||||
 | 
					   <addaction name="actionNew_database"/>
 | 
				
			||||||
 | 
					   <addaction name="actionOpen_database"/>
 | 
				
			||||||
 | 
					   <addaction name="separator"/>
 | 
				
			||||||
 | 
					  </widget>
 | 
				
			||||||
 | 
					  <widget class="QStatusBar" name="statusBar"/>
 | 
				
			||||||
 | 
					  <action name="actionAbout_qbolt">
 | 
				
			||||||
 | 
					   <property name="icon">
 | 
				
			||||||
 | 
					    <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					     <normaloff>:/rsrc/information.png</normaloff>:/rsrc/information.png</iconset>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>&About QBolt</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionAbout_Qt">
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>About &Qt</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionOpen_database">
 | 
				
			||||||
 | 
					   <property name="icon">
 | 
				
			||||||
 | 
					    <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					     <normaloff>:/rsrc/database.png</normaloff>:/rsrc/database.png</iconset>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>&Open database...</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="shortcut">
 | 
				
			||||||
 | 
					    <string>Ctrl+O</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionExit">
 | 
				
			||||||
 | 
					   <property name="icon">
 | 
				
			||||||
 | 
					    <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					     <normaloff>:/rsrc/door_out.png</normaloff>:/rsrc/door_out.png</iconset>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>&Exit</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionDisconnect">
 | 
				
			||||||
 | 
					   <property name="icon">
 | 
				
			||||||
 | 
					    <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					     <normaloff>:/rsrc/disconnect.png</normaloff>:/rsrc/disconnect.png</iconset>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>Disconnect</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionDelete_bucket">
 | 
				
			||||||
 | 
					   <property name="icon">
 | 
				
			||||||
 | 
					    <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					     <normaloff>:/rsrc/table_delete.png</normaloff>:/rsrc/table_delete.png</iconset>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>Delete bucket</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionRefresh_buckets">
 | 
				
			||||||
 | 
					   <property name="icon">
 | 
				
			||||||
 | 
					    <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					     <normaloff>:/rsrc/arrow_refresh.png</normaloff>:/rsrc/arrow_refresh.png</iconset>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>Refresh buckets</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionClear_selection">
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>&Clear selection</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionNew_database">
 | 
				
			||||||
 | 
					   <property name="icon">
 | 
				
			||||||
 | 
					    <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					     <normaloff>:/rsrc/database_add.png</normaloff>:/rsrc/database_add.png</iconset>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>&New database...</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionAdd_bucket">
 | 
				
			||||||
 | 
					   <property name="icon">
 | 
				
			||||||
 | 
					    <iconset resource="resources.qrc">
 | 
				
			||||||
 | 
					     <normaloff>:/rsrc/table_add.png</normaloff>:/rsrc/table_add.png</iconset>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>Add bucket...</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					  <action name="actionOpen_database_as_read_only">
 | 
				
			||||||
 | 
					   <property name="text">
 | 
				
			||||||
 | 
					    <string>Open database as read-only...</string>
 | 
				
			||||||
 | 
					   </property>
 | 
				
			||||||
 | 
					  </action>
 | 
				
			||||||
 | 
					 </widget>
 | 
				
			||||||
 | 
					 <layoutdefault spacing="6" margin="11"/>
 | 
				
			||||||
 | 
					 <resources>
 | 
				
			||||||
 | 
					  <include location="resources.qrc"/>
 | 
				
			||||||
 | 
					 </resources>
 | 
				
			||||||
 | 
					 <connections/>
 | 
				
			||||||
 | 
					</ui>
 | 
				
			||||||
							
								
								
									
										48
									
								
								qbolt/qbolt.pro
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					#-------------------------------------------------
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Project created by QtCreator 2017-05-15T19:38:33
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#-------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QT       += core gui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TARGET = qbolt
 | 
				
			||||||
 | 
					TEMPLATE = app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Enforce Qt deprecations
 | 
				
			||||||
 | 
					DEFINES += QT_DEPRECATED_WARNINGS
 | 
				
			||||||
 | 
					DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					win32: {
 | 
				
			||||||
 | 
					    # for some reason, qbolt_cgo.h never realises that Q_OS_WIN is defined for win32 builds... weird
 | 
				
			||||||
 | 
					    DEFINES += CGO_WINDOWS
 | 
				
			||||||
 | 
					    QMAKE_LIBS += $$_PRO_FILE_PWD_/../build/win32/qbolt.a
 | 
				
			||||||
 | 
					    QMAKE_LIBS += -lntdll
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RC_ICONS = rsrc/qbolt.ico
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					linux: {
 | 
				
			||||||
 | 
					    QMAKE_LIBS += $$_PRO_FILE_PWD_/../build/linux/qbolt.a
 | 
				
			||||||
 | 
					    QMAKE_LIBS += -lpthread
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SOURCES += main.cpp\
 | 
				
			||||||
 | 
					        mainwindow.cpp \
 | 
				
			||||||
 | 
					    interop.cpp \
 | 
				
			||||||
 | 
					    boltdb.cpp \
 | 
				
			||||||
 | 
					    itemwindow.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HEADERS  += mainwindow.h \
 | 
				
			||||||
 | 
					    interop.h \
 | 
				
			||||||
 | 
					    boltdb.h \
 | 
				
			||||||
 | 
					    qbolt_cgo.h \
 | 
				
			||||||
 | 
					    itemwindow.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORMS    += mainwindow.ui \
 | 
				
			||||||
 | 
					    itemwindow.ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RESOURCES += \
 | 
				
			||||||
 | 
					    resources.qrc
 | 
				
			||||||
							
								
								
									
										10
									
								
								qbolt/qbolt_cgo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					#ifndef QBOLT_CGO_H
 | 
				
			||||||
 | 
					#define QBOLT_CGO_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(Q_OS_WIN) || defined(CGO_WINDOWS)
 | 
				
			||||||
 | 
					#include "../build/win32/qbolt.h"
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#include "../build/linux/qbolt.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // QBOLT_CGO_H
 | 
				
			||||||
							
								
								
									
										17
									
								
								qbolt/resources.qrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<RCC>
 | 
				
			||||||
 | 
					    <qresource prefix="/">
 | 
				
			||||||
 | 
					        <file>rsrc/database_add.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/table.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/information.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/database_lightning.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/database.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/table_add.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/table_delete.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/add.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/delete.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/chart_bar.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/arrow_refresh.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/disconnect.png</file>
 | 
				
			||||||
 | 
					        <file>rsrc/door_out.png</file>
 | 
				
			||||||
 | 
					    </qresource>
 | 
				
			||||||
 | 
					</RCC>
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/add.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 733 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/arrow_refresh.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 685 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/chart_bar.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 541 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/database.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 390 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/database_add.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 658 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/database_connect.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 763 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/database_lightning.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 775 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/delete.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 715 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/disconnect.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 796 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/door_out.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 688 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/information.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 778 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/page.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 635 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/qbolt.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/table.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 566 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/table_add.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 663 B  | 
							
								
								
									
										
											BIN
										
									
								
								qbolt/rsrc/table_delete.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 660 B  |