Compare commits
	
		
			105 Commits
		
	
	
		
			archive/co
			...
			v1.1.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4e13d8dffd | |||
| 20e5efa711 | |||
| 6378740051 | |||
| b8ce7a667b | |||
| 281ca18d90 | |||
| 1725de6ace | |||
| 0ad7c03db0 | |||
| 1cdd0d113b | |||
| a0fae43690 | |||
| 04ac766125 | |||
| 9ac26467c0 | |||
| cbfc038839 | |||
| 7ae6462da0 | |||
| 09a3e5b90f | |||
| 1cddd17017 | |||
| 8fdc3a0428 | |||
| 18f10fc1b4 | |||
| 18331ae007 | |||
| f08242e93c | |||
| 23965230e2 | |||
| e43b261752 | |||
| f9b4cb71a5 | |||
| 96c05641bd | |||
| 2c5d1946ec | |||
| a6cbc5a9ed | |||
| 6008ae44a2 | |||
| 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 | 
							
								
								
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,16 @@
 | 
			
		||||
# development
 | 
			
		||||
qbolt
 | 
			
		||||
dummy-data/dummy-data*
 | 
			
		||||
 | 
			
		||||
# temporary build files
 | 
			
		||||
rsrc_windows_amd64.syso
 | 
			
		||||
windows-manifest.json
 | 
			
		||||
 | 
			
		||||
# release build files
 | 
			
		||||
build/qbolt
 | 
			
		||||
build/qbolt.exe
 | 
			
		||||
build/*.xz
 | 
			
		||||
build/*.zip
 | 
			
		||||
 | 
			
		||||
# local makefile definition scripts
 | 
			
		||||
make-*
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										67
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,67 @@
 | 
			
		||||
VERSION := 1.1.0
 | 
			
		||||
GOFLAGS_L := -ldflags='-s -w -X main.Version=v$(VERSION)' -buildvcs=false -gcflags='-trimpath=$(CURDIR)' -asmflags='-trimpath=$(CURDIR)'
 | 
			
		||||
GOFLAGS_W := -ldflags='-s -w -X main.Version=v$(VERSION) -H windowsgui' -buildvcs=false --tags=windowsqtstatic -gcflags='-trimpath=$(CURDIR)' -asmflags='-trimpath=$(CURDIR)'
 | 
			
		||||
SHELL := /bin/bash
 | 
			
		||||
GO := go
 | 
			
		||||
MIQT_DOCKER := miqt-docker
 | 
			
		||||
MIQT_UIC := miqt-uic
 | 
			
		||||
MIQT_RCC := miqt-rcc
 | 
			
		||||
GO_WINRES := go-winres
 | 
			
		||||
SOURCES := $(wildcard *.go *.ui *.qrc) resources.go resources.rcc mainwindow_ui.go itemwindow_ui.go rsrc_windows_amd64.syso
 | 
			
		||||
 | 
			
		||||
.PHONY: all
 | 
			
		||||
all: build/qbolt build/qbolt.exe
 | 
			
		||||
	
 | 
			
		||||
.PHONY: dist
 | 
			
		||||
dist: build/qbolt-${VERSION}-windows-x86_64.zip build/qbolt-${VERSION}-debian12-x86_64.tar.xz
 | 
			
		||||
 | 
			
		||||
.PHONY: clean
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f qbolt
 | 
			
		||||
	rm -rf build
 | 
			
		||||
	mkdir -p build
 | 
			
		||||
	touch build/.create_dir
 | 
			
		||||
	rm -f windows-manifest.json
 | 
			
		||||
	rm -f rsrc_windows_amd64.syso
 | 
			
		||||
	rm -f resources.go
 | 
			
		||||
	rm -f resources.rcc
 | 
			
		||||
	
 | 
			
		||||
# Generated files
 | 
			
		||||
	
 | 
			
		||||
resources.rcc resources.go: resources.qrc
 | 
			
		||||
	$(MIQT_RCC) -Qt6 -Input resources.qrc
 | 
			
		||||
	
 | 
			
		||||
mainwindow_ui.go: mainwindow.ui
 | 
			
		||||
	$(MIQT_UIC) -Qt6 -InFile mainwindow.ui -OutFile mainwindow_ui.go
 | 
			
		||||
 | 
			
		||||
itemwindow_ui.go: itemwindow.ui
 | 
			
		||||
	$(MIQT_UIC) -Qt6 -InFile itemwindow.ui -OutFile itemwindow_ui.go
 | 
			
		||||
	
 | 
			
		||||
windows-manifest.json: windows-manifest.template.json Makefile
 | 
			
		||||
	cat windows-manifest.template.json | sed -re 's_%VERSION%_$(VERSION)_' > windows-manifest.json
 | 
			
		||||
	
 | 
			
		||||
rsrc_windows_amd64.syso: windows-manifest.json
 | 
			
		||||
	$(GO_WINRES) make --in windows-manifest.json
 | 
			
		||||
	rm rsrc_windows_386.syso || true # we do not build x86_32
 | 
			
		||||
	
 | 
			
		||||
# Linux release
 | 
			
		||||
	
 | 
			
		||||
build/qbolt: $(SOURCES)
 | 
			
		||||
	CGO_CFLAGS='-Os -ffunction-sections -fdata-sections -flto=auto' CGO_CXXFLAGS='-Os -ffunction-sections -fdata-sections -flto=auto' CGO_LDFLAGS='-Wl,--gc-sections -flto=auto -fwhole-program' $(GO) build $(GOFLAGS_L) -o build/qbolt
 | 
			
		||||
	upx build/qbolt
 | 
			
		||||
 | 
			
		||||
build/qbolt-${VERSION}-debian12-x86_64.tar.xz: build/qbolt
 | 
			
		||||
	XZ_OPTS=-9e tar caf build/qbolt-${VERSION}-debian12-x86_64.tar.xz -C build qbolt --owner=0 --group=0
 | 
			
		||||
	
 | 
			
		||||
# Windows release (docker)
 | 
			
		||||
 | 
			
		||||
build/qbolt.exe: $(SOURCES)
 | 
			
		||||
	# -flto causes internal compiler error
 | 
			
		||||
	$(MIQT_DOCKER) win64-qt6-static /bin/bash -c "CGO_CFLAGS='-Os -ffunction-sections -fdata-sections' CGO_CXXFLAGS='-Os -ffunction-sections -fdata-sections' CGO_LDFLAGS='-Wl,--gc-sections -fwhole-program' go build $(GOFLAGS_W) -o build/qbolt.exe"
 | 
			
		||||
	# Must be stripped before upx'ing - @ref https://github.com/msys2/MSYS2-packages/issues/454
 | 
			
		||||
	# However this removes the rsrc, loses the icon and causes a Defender detection
 | 
			
		||||
	# strip build/qbolt.exe
 | 
			
		||||
	# upx build/qbolt.exe
 | 
			
		||||
 | 
			
		||||
build/qbolt-${VERSION}-windows-x86_64.zip: build/qbolt.exe
 | 
			
		||||
	zip -9 -j build/qbolt-${VERSION}-windows-x86_64.zip build/qbolt.exe
 | 
			
		||||
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@@ -1,12 +1,10 @@
 | 
			
		||||
# qbolt
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
A graphical database manager for BoltDB.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
Written in Golang (Qt)
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
@@ -24,25 +22,41 @@ Source code content of `qbolt-x.x.x-src.tar.gz` is released under the ISC licens
 | 
			
		||||
BoltDB is released under the MIT license.
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
## Changelog
 | 
			
		||||
 | 
			
		||||
2025-05-04 1.1.0
 | 
			
		||||
- New feature to import/export database as zip archive
 | 
			
		||||
- Upgrade to Qt 6
 | 
			
		||||
- Add keyboard shortcuts for refresh
 | 
			
		||||
- Improve High DPI support
 | 
			
		||||
- Rebuild artefacts with miqt v0.10.0, etcd-io/bbolt v1.4.0, go 1.23, Qt 6.8 (win64)
 | 
			
		||||
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.1.0)
 | 
			
		||||
 | 
			
		||||
2024-10-05 1.0.3
 | 
			
		||||
- Port from hybrid Go/C++ to now using [MIQT](https://github.com/mappu/miqt)
 | 
			
		||||
- Switch Windows build to win64
 | 
			
		||||
- Rebuild artefacts with miqt v0.5.0, etcd-io/bbolt v1.3.11, go 1.19 (deb12), go 1.23 (win64)
 | 
			
		||||
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.3)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
- Feature: Option to open database as read-only
 | 
			
		||||
- Fix an issue with support for bucket names and keys not surviving UTF-8 roundtrips (now binary-clean)
 | 
			
		||||
- Fix an issue with crashing when deleting a bucket other than the selected one
 | 
			
		||||
- Fix a cosmetic issue with application icon on Windows
 | 
			
		||||
- [⬇️ qbolt-1.0.1-win32.zip](dist-archive/qbolt-1.0.1-win32.zip) *(5.07 MiB)*
 | 
			
		||||
- [⬇️ 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)*
 | 
			
		||||
 | 
			
		||||
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.1)
 | 
			
		||||
 | 
			
		||||
2017-05-21 1.0.0
 | 
			
		||||
- Initial public release
 | 
			
		||||
- [⬇️ qbolt-1.0.0-win32.zip](dist-archive/qbolt-1.0.0-win32.zip) *(5.07 MiB)*
 | 
			
		||||
- [⬇️ 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)*
 | 
			
		||||
 | 
			
		||||
- 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.
 | 
			
		||||
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.0)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										223
									
								
								bolt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,223 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	bolt "go.etcd.io/bbolt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Bolt_Open(readOnly bool, path string) (*bolt.DB, error) {
 | 
			
		||||
	opts := *bolt.DefaultOptions
 | 
			
		||||
	opts.Timeout = 10 * time.Second
 | 
			
		||||
	opts.ReadOnly = readOnly
 | 
			
		||||
 | 
			
		||||
	return bolt.Open(path, os.FileMode(0644), &opts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(db *bolt.DB, browse []string, fn func(tx *bolt.Tx, bucket *bolt.Bucket) error) error {
 | 
			
		||||
	if len(browse) == 0 {
 | 
			
		||||
		// not a bucket
 | 
			
		||||
		return errors.New("No bucket selected")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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(tx, bucket)
 | 
			
		||||
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_CreateBucket(db *bolt.DB, browse []string, newBucket string) 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
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_DeleteBucket(db *bolt.DB, browse []string, delBucket string) 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))
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_SetItem(db *bolt.DB, browse []string, key, val []byte) error {
 | 
			
		||||
	if len(browse) == 0 {
 | 
			
		||||
		return errors.New("Can't create top-level items")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
 | 
			
		||||
		bucket, err := walkBuckets(tx, browse)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return bucket.Put(key, val)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_DeleteItem(db *bolt.DB, browse []string, key []byte) error {
 | 
			
		||||
	if len(browse) == 0 {
 | 
			
		||||
		return errors.New("Can't create top-level items")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
 | 
			
		||||
		bucket, err := walkBuckets(tx, browse)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return bucket.Delete(key)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_DBStats(db *bolt.DB) (string, error) {
 | 
			
		||||
	jBytes, err := json.MarshalIndent(db.Stats(), "", "  ")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return string(jBytes), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_BucketStats(db *bolt.DB, browse []string) (string, error) {
 | 
			
		||||
	var stats bolt.BucketStats
 | 
			
		||||
 | 
			
		||||
	err := withBrowse_ReadOnly(db, browse, func(tx *bolt.Tx, bucket *bolt.Bucket) error {
 | 
			
		||||
		stats = bucket.Stats()
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	jBytes, err := json.MarshalIndent(stats, "", "  ")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return string(jBytes), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_ListBuckets(db *bolt.DB, browse []string, cb func(b string) error) error {
 | 
			
		||||
 | 
			
		||||
	if len(browse) == 0 {
 | 
			
		||||
		// root mode
 | 
			
		||||
		return db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
			return tx.ForEach(func(k []byte, _ *bolt.Bucket) error {
 | 
			
		||||
				return cb(string(k))
 | 
			
		||||
			})
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Nested-mode
 | 
			
		||||
	return withBrowse_ReadOnly(db, browse, func(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 {
 | 
			
		||||
				return cb(string(k))
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ListItemInfo struct {
 | 
			
		||||
	Name    []byte
 | 
			
		||||
	DataLen int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_ListItems(db *bolt.DB, browse []string, cb func(ListItemInfo) error) error {
 | 
			
		||||
 | 
			
		||||
	if len(browse) == 0 {
 | 
			
		||||
		return errors.New("No bucket specified")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Nested-mode
 | 
			
		||||
	return withBrowse_ReadOnly(db, browse, func(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
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			kcopy := make([]byte, len(k))
 | 
			
		||||
			copy(kcopy, k)
 | 
			
		||||
 | 
			
		||||
			return cb(ListItemInfo{kcopy, int64(len(v))})
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_GetItem(db *bolt.DB, browse []string, key []byte) ([]byte, error) {
 | 
			
		||||
	var ret []byte
 | 
			
		||||
 | 
			
		||||
	err := withBrowse_ReadOnly(db, browse, func(tx *bolt.Tx, bucket *bolt.Bucket) error {
 | 
			
		||||
		d := bucket.Get([]byte(key))
 | 
			
		||||
 | 
			
		||||
		ret = make([]byte, len(d))
 | 
			
		||||
		copy(ret, d)
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	return ret, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_Close(db *bolt.DB) error {
 | 
			
		||||
	return db.Close()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								build/.create_dir
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										82
									
								
								dummy-data/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,82 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	bolt "go.etcd.io/bbolt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										167
									
								
								export.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,167 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"archive/zip"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/fs"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Bolt_ExportDatabaseToZip(dbpath, zippath string) error {
 | 
			
		||||
	db, err := Bolt_Open(true, dbpath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Error opening database: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer db.Close()
 | 
			
		||||
 | 
			
		||||
	fh, err := os.OpenFile(zippath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Error opening output file: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer fh.Close()
 | 
			
		||||
 | 
			
		||||
	zw := zip.NewWriter(fh)
 | 
			
		||||
 | 
			
		||||
	// Filenames in zip files cannot contain `/` characters. Mangle it
 | 
			
		||||
	safename := func(n string) string {
 | 
			
		||||
		return strings.ReplaceAll(string(n), `/`, `__`)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var process func(currentPath []string) error
 | 
			
		||||
	process = func(currentPath []string) error {
 | 
			
		||||
		return Bolt_ListBuckets(db, currentPath, func(bucket string) error {
 | 
			
		||||
 | 
			
		||||
			// Create entry for our own bucket
 | 
			
		||||
 | 
			
		||||
			ourBucket := zip.FileHeader{
 | 
			
		||||
				Name: path.Join(path.Join(Apply(currentPath, safename)...), safename(bucket)) + `/`, // Trailing slash = directory
 | 
			
		||||
			}
 | 
			
		||||
			ourBucket.SetMode(fs.ModeDir | 0755)
 | 
			
		||||
			_, err := zw.CreateHeader(&ourBucket)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Child pathspec
 | 
			
		||||
 | 
			
		||||
			childPath := CopySliceAdd(currentPath, bucket)
 | 
			
		||||
 | 
			
		||||
			// Create file entries for all non-bucket children
 | 
			
		||||
 | 
			
		||||
			err = Bolt_ListItems(db, childPath, func(li ListItemInfo) error {
 | 
			
		||||
				fileItem := zip.FileHeader{
 | 
			
		||||
					Name: path.Join(path.Join(Apply(childPath, safename)...), safename(string(li.Name))),
 | 
			
		||||
				}
 | 
			
		||||
				fileItem.SetMode(0644)
 | 
			
		||||
				fileW, err := zw.CreateHeader(&fileItem)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				buff, err := Bolt_GetItem(db, childPath, []byte(li.Name))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				_, err = io.CopyN(fileW, bytes.NewReader(buff), li.DataLen)
 | 
			
		||||
				return err
 | 
			
		||||
			})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Recurse for all bucket-type children
 | 
			
		||||
 | 
			
		||||
			process(childPath)
 | 
			
		||||
 | 
			
		||||
			// Done
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = process([]string{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = zw.Flush()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = zw.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fh.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bolt_ImportZipToDatabase(dbpath, zippath string) error {
 | 
			
		||||
 | 
			
		||||
	db, err := Bolt_Open(false, dbpath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Error opening target database: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer db.Close()
 | 
			
		||||
 | 
			
		||||
	fh, err := os.OpenFile(zippath, os.O_RDONLY, 0400)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Error opening input archive: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer fh.Close()
 | 
			
		||||
 | 
			
		||||
	fstat, err := fh.Stat()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zr, err := zip.NewReader(fh, fstat.Size())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Reading zip file format: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, zf := range zr.File {
 | 
			
		||||
		if strings.HasSuffix(zf.Name, `/`) || (zf.Mode()&fs.ModeDir) != 0 {
 | 
			
		||||
			// Bucket
 | 
			
		||||
			bucketPath := strings.Split(strings.TrimSuffix(zf.Name, `/`), `/`)
 | 
			
		||||
			err = Bolt_CreateBucket(db, bucketPath[0:len(bucketPath)-1], bucketPath[len(bucketPath)-1])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("Creating bucket %q: %w", zf.Name, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			// Object
 | 
			
		||||
			objectPath := strings.Split(zf.Name, `/`)
 | 
			
		||||
 | 
			
		||||
			rc, err := zf.Open()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			content, err := io.ReadAll(rc)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err = Bolt_SetItem(db, objectPath[0:len(objectPath)-1], []byte(objectPath[len(objectPath)-1]), content)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err = rc.Close()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Done
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,12 @@
 | 
			
		||||
module code.ivysaur.me/qbolt
 | 
			
		||||
 | 
			
		||||
go 1.23.0
 | 
			
		||||
 | 
			
		||||
toolchain go1.23.3
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/mappu/miqt v0.10.0
 | 
			
		||||
	go.etcd.io/bbolt v1.4.0
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require golang.org/x/sys v0.32.0 // indirect
 | 
			
		||||
							
								
								
									
										20
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,20 @@
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/mappu/miqt v0.10.0 h1:w+ucRwdoIO7xS32us34lL2Mh0+aarywNpQz6c76ZSDY=
 | 
			
		||||
github.com/mappu/miqt v0.10.0/go.mod h1:xFg7ADaO1QSkmXPsPODoKe/bydJpRG9fgCYyIDl/h1U=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 | 
			
		||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 | 
			
		||||
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
 | 
			
		||||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
 | 
			
		||||
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
 | 
			
		||||
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
 | 
			
		||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
 | 
			
		||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 | 
			
		||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
 | 
			
		||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
 | 
			
		||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
							
								
								
									
										95
									
								
								itemwindow.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,95 @@
 | 
			
		||||
<?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">
 | 
			
		||||
     <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>
 | 
			
		||||
							
								
								
									
										77
									
								
								itemwindow_ui.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,77 @@
 | 
			
		||||
// Generated by miqt-uic. To update this file, edit the .ui file in
 | 
			
		||||
// Qt Designer, and then run 'go generate'.
 | 
			
		||||
//
 | 
			
		||||
//go:generate miqt-uic -Qt6 -InFile itemwindow.ui -OutFile itemwindow_ui.go
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	qt "github.com/mappu/miqt/qt6"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ItemWindowUi struct {
 | 
			
		||||
	ItemWindow   *qt.QDialog
 | 
			
		||||
	gridLayout_2 *qt.QGridLayout
 | 
			
		||||
	contentArea  *qt.QPlainTextEdit
 | 
			
		||||
	frame        *qt.QFrame
 | 
			
		||||
	gridLayout   *qt.QGridLayout
 | 
			
		||||
	buttonBox    *qt.QDialogButtonBox
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewItemWindowUi creates all Qt widget classes for ItemWindow.
 | 
			
		||||
func NewItemWindowUi() *ItemWindowUi {
 | 
			
		||||
	ui := &ItemWindowUi{}
 | 
			
		||||
	ui.ItemWindow = qt.NewQDialog(nil)
 | 
			
		||||
	ItemWindow__objectName := qt.NewQAnyStringView3("ItemWindow")
 | 
			
		||||
	ui.ItemWindow.SetObjectName(*ItemWindow__objectName)
 | 
			
		||||
	ItemWindow__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.ItemWindow.Resize(370, 353)
 | 
			
		||||
	ui.ItemWindow.SetWindowTitle("")
 | 
			
		||||
	icon0 := qt.NewQIcon()
 | 
			
		||||
	icon0.AddFile4(":/rsrc/database_lightning.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.ItemWindow.SetWindowIcon(icon0)
 | 
			
		||||
	ui.gridLayout_2 = qt.NewQGridLayout(ui.ItemWindow.QWidget)
 | 
			
		||||
	gridLayout_2__objectName := qt.NewQAnyStringView3("gridLayout_2")
 | 
			
		||||
	ui.gridLayout_2.SetObjectName(*gridLayout_2__objectName)
 | 
			
		||||
	gridLayout_2__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_2.SetVerticalSpacing(0)
 | 
			
		||||
	ui.gridLayout_2.SetContentsMargins(0, 0, 0, 0)
 | 
			
		||||
	ui.gridLayout_2.SetSpacing(6)
 | 
			
		||||
	ui.contentArea = qt.NewQPlainTextEdit(ui.ItemWindow.QWidget)
 | 
			
		||||
	contentArea__objectName := qt.NewQAnyStringView3("contentArea")
 | 
			
		||||
	ui.contentArea.SetObjectName(*contentArea__objectName)
 | 
			
		||||
	contentArea__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.contentArea.SetFrameShape(qt.QFrame__NoFrame)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_2.AddWidget2(ui.contentArea.QWidget, 0, 0)
 | 
			
		||||
	ui.frame = qt.NewQFrame(ui.ItemWindow.QWidget)
 | 
			
		||||
	frame__objectName := qt.NewQAnyStringView3("frame")
 | 
			
		||||
	ui.frame.SetObjectName(*frame__objectName)
 | 
			
		||||
	frame__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout = qt.NewQGridLayout(ui.frame.QWidget)
 | 
			
		||||
	gridLayout__objectName := qt.NewQAnyStringView3("gridLayout")
 | 
			
		||||
	ui.gridLayout.SetObjectName(*gridLayout__objectName)
 | 
			
		||||
	gridLayout__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout.SetContentsMargins(11, 11, 11, 11)
 | 
			
		||||
	ui.gridLayout.SetSpacing(6)
 | 
			
		||||
	ui.buttonBox = qt.NewQDialogButtonBox(ui.frame.QWidget)
 | 
			
		||||
	buttonBox__objectName := qt.NewQAnyStringView3("buttonBox")
 | 
			
		||||
	ui.buttonBox.SetObjectName(*buttonBox__objectName)
 | 
			
		||||
	buttonBox__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.buttonBox.SetStandardButtons(qt.QDialogButtonBox__Cancel | qt.QDialogButtonBox__Save)
 | 
			
		||||
	ui.buttonBox.OnAccepted(ui.ItemWindow.Accept)
 | 
			
		||||
	ui.buttonBox.OnRejected(ui.ItemWindow.Reject)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout.AddWidget2(ui.buttonBox.QWidget, 0, 0)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_2.AddWidget2(ui.frame.QWidget, 1, 0)
 | 
			
		||||
 | 
			
		||||
	ui.Retranslate()
 | 
			
		||||
 | 
			
		||||
	return ui
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Retranslate reapplies all text translations.
 | 
			
		||||
func (ui *ItemWindowUi) Retranslate() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,28 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	qt "github.com/mappu/miqt/qt6"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Version string = "v0.0.0-devel"
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
 | 
			
		||||
	_ = qt.NewQApplication(os.Args)
 | 
			
		||||
 | 
			
		||||
	qt.QGuiApplication_SetApplicationDisplayName("QBolt")
 | 
			
		||||
	qt.QGuiApplication_SetWindowIcon(qt.NewQIcon4(":/rsrc/database_lightning.png"))
 | 
			
		||||
 | 
			
		||||
	// High DPI tweaks
 | 
			
		||||
	qt.QCoreApplication_SetAttribute2(qt.AA_EnableHighDpiScaling, true)
 | 
			
		||||
	qt.QCoreApplication_SetAttribute2(qt.AA_UseHighDpiPixmaps, true)
 | 
			
		||||
	qt.QCoreApplication_SetAttribute2(qt.AA_Use96Dpi, true)
 | 
			
		||||
	qt.QGuiApplication_SetHighDpiScaleFactorRoundingPolicy(qt.PassThrough)
 | 
			
		||||
 | 
			
		||||
	w := NewMainWindow()
 | 
			
		||||
	w.ui.MainWindow.Show()
 | 
			
		||||
 | 
			
		||||
	qt.QApplication_Exec()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										586
									
								
								mainwindow.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,586 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	qt "github.com/mappu/miqt/qt6"
 | 
			
		||||
	"github.com/mappu/miqt/qt6/mainthread"
 | 
			
		||||
	bolt "go.etcd.io/bbolt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MainWindow struct {
 | 
			
		||||
	ui *MainWindowUi
 | 
			
		||||
 | 
			
		||||
	databaseContext      *qt.QMenu
 | 
			
		||||
	bucketContext        *qt.QMenu
 | 
			
		||||
	lastContextSelection *qt.QTreeWidgetItem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMainWindow() *MainWindow {
 | 
			
		||||
	this := &MainWindow{}
 | 
			
		||||
	this.ui = NewMainWindowUi()
 | 
			
		||||
 | 
			
		||||
	this.on_bucketTree_currentItemChanged(nil, nil)
 | 
			
		||||
 | 
			
		||||
	this.databaseContext = qt.NewQMenu(this.ui.MainWindow.QWidget)
 | 
			
		||||
	this.databaseContext.QWidget.AddAction(this.ui.actionRefresh_buckets)
 | 
			
		||||
	this.databaseContext.QWidget.AddAction(this.ui.actionAdd_bucket)
 | 
			
		||||
	this.databaseContext.AddSeparator()
 | 
			
		||||
	this.databaseContext.QWidget.AddAction(this.ui.actionDisconnect)
 | 
			
		||||
 | 
			
		||||
	this.bucketContext = qt.NewQMenu(this.ui.MainWindow.QWidget)
 | 
			
		||||
	this.bucketContext.QWidget.AddAction(this.ui.actionRefresh_buckets)
 | 
			
		||||
	this.bucketContext.QWidget.AddAction(this.ui.actionAdd_bucket)
 | 
			
		||||
	this.bucketContext.AddSeparator()
 | 
			
		||||
	this.bucketContext.QWidget.AddAction(this.ui.actionDelete_bucket)
 | 
			
		||||
 | 
			
		||||
	// Connections
 | 
			
		||||
	this.ui.actionNew_database.OnTriggered(this.on_actionNew_database_triggered)
 | 
			
		||||
	this.ui.actionOpen_database.OnTriggered(this.on_actionOpen_database_triggered)
 | 
			
		||||
	this.ui.actionOpen_database_as_read_only.OnTriggered(this.on_actionOpen_database_as_read_only_triggered)
 | 
			
		||||
	this.ui.actionExport_database_as_zip.OnTriggered(this.onactionExport_database_as_zip_triggered)
 | 
			
		||||
	this.ui.actionCreate_database_from_zip.OnTriggered(this.onactionCreate_database_from_zip_triggered)
 | 
			
		||||
	this.ui.actionExit.OnTriggered(this.on_actionExit_triggered)
 | 
			
		||||
	this.ui.actionAbout_Qt.OnTriggered(this.on_actionAbout_Qt_triggered)
 | 
			
		||||
	this.ui.actionAbout_qbolt.OnTriggered(this.on_actionAbout_qbolt_triggered)
 | 
			
		||||
	this.ui.actionDisconnect.OnTriggered(this.on_actionDisconnect_triggered)
 | 
			
		||||
	this.ui.bucketTree.OnCustomContextMenuRequested(this.on_bucketTree_customContextMenuRequested)
 | 
			
		||||
	this.ui.actionRefresh_buckets.OnTriggered(this.on_actionRefresh_buckets_triggered)
 | 
			
		||||
	this.ui.bucketTree.OnCurrentItemChanged(this.on_bucketTree_currentItemChanged)
 | 
			
		||||
	this.ui.actionClear_selection.OnTriggered(this.on_actionClear_selection_triggered)
 | 
			
		||||
	this.ui.bucketData.OnDoubleClicked(this.on_bucketData_doubleClicked)
 | 
			
		||||
	this.ui.actionAdd_bucket.OnTriggered(this.on_actionAdd_bucket_triggered)
 | 
			
		||||
	this.ui.actionDelete_bucket.OnTriggered(this.on_actionDelete_bucket_triggered)
 | 
			
		||||
	this.ui.AddDataButton.OnClicked(this.on_AddDataButton_clicked)
 | 
			
		||||
	this.ui.DeleteDataButton.OnClicked(this.on_DeleteDataButton_clicked)
 | 
			
		||||
	this.ui.bucketData.OnItemSelectionChanged(this.on_bucketData_itemSelectionChanged)
 | 
			
		||||
 | 
			
		||||
	this.ui.MainWindow.SetAcceptDrops(true)
 | 
			
		||||
	this.ui.MainWindow.OnDragEnterEvent(this.onDragEnter)
 | 
			
		||||
	this.ui.MainWindow.OnDragMoveEvent(this.onDragMove)
 | 
			
		||||
	this.ui.MainWindow.OnDropEvent(this.onDropEvent)
 | 
			
		||||
 | 
			
		||||
	return this
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	BdbPointerRole = int(qt.UserRole + 1)
 | 
			
		||||
	BinaryDataRole = int(qt.UserRole + 2)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var bdbs []*bolt.DB = nil
 | 
			
		||||
 | 
			
		||||
func SET_BDB(top *qt.QTreeWidgetItem, bdb *bolt.DB) {
 | 
			
		||||
	idx := len(bdbs)
 | 
			
		||||
	bdbs = append(bdbs, bdb)
 | 
			
		||||
	top.SetData(0, BdbPointerRole, qt.NewQVariant4(idx)) // Don't store a Go pointer in Qt memory
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GET_BDB(top *qt.QTreeWidgetItem) *bolt.DB {
 | 
			
		||||
	if top == nil {
 | 
			
		||||
		panic("Passed a nil QTreeWidgetItem")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dataVariant := top.Data(0, BdbPointerRole)
 | 
			
		||||
	if dataVariant == nil {
 | 
			
		||||
		panic("Selected item has no bdb")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bdbs[dataVariant.ToInt()]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) Widget() *qt.QWidget {
 | 
			
		||||
	return this.ui.centralWidget
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) onDragEnter(super func(event *qt.QDragEnterEvent), event *qt.QDragEnterEvent) {
 | 
			
		||||
	if !event.MimeData().HasUrls() {
 | 
			
		||||
		event.Ignore()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	event.Accept()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) onDragMove(super func(event *qt.QDragMoveEvent), event *qt.QDragMoveEvent) {
 | 
			
		||||
	if !event.MimeData().HasUrls() {
 | 
			
		||||
		event.Ignore()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	event.SetDropAction(qt.CopyAction)
 | 
			
		||||
	event.Accept()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) onDropEvent(super func(event *qt.QDropEvent), event *qt.QDropEvent) {
 | 
			
		||||
	if !event.MimeData().HasUrls() {
 | 
			
		||||
		event.Ignore()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	event.Accept()
 | 
			
		||||
	paths := event.MimeData().Urls()
 | 
			
		||||
	for _, path := range paths {
 | 
			
		||||
		lpath := path.ToLocalFile()
 | 
			
		||||
		this.openDatabase(lpath, false)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionNew_database_triggered() {
 | 
			
		||||
	file := qt.QFileDialog_GetSaveFileName2(this.Widget(), "Save new bolt database as...")
 | 
			
		||||
	if len(file) > 0 {
 | 
			
		||||
		this.openDatabase(file, false)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionOpen_database_triggered() {
 | 
			
		||||
	file := qt.QFileDialog_GetOpenFileName2(this.Widget(), "Select bolt database...")
 | 
			
		||||
	if len(file) > 0 {
 | 
			
		||||
		this.openDatabase(file, false)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionOpen_database_as_read_only_triggered() {
 | 
			
		||||
	file := qt.QFileDialog_GetOpenFileName2(this.Widget(), "Select bolt database...")
 | 
			
		||||
	if len(file) > 0 {
 | 
			
		||||
		this.openDatabase(file, true)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) alert(message string) {
 | 
			
		||||
	qt.QMessageBox_Critical(this.Widget(), "qbolt", message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) openDatabase(file string, readOnly bool) {
 | 
			
		||||
 | 
			
		||||
	// Open
 | 
			
		||||
	bdb, err := Bolt_Open(readOnly, file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.alert(fmt.Sprintf("Error opening database: %s", err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	top := qt.NewQTreeWidgetItem()
 | 
			
		||||
	top.SetText(0, filepath.Base(file))
 | 
			
		||||
	top.SetIcon(0, qt.NewQIcon4(":/rsrc/database.png"))
 | 
			
		||||
	SET_BDB(top, bdb)
 | 
			
		||||
	this.ui.bucketTree.AddTopLevelItem(top)
 | 
			
		||||
 | 
			
		||||
	this.refreshBucketTree(top)
 | 
			
		||||
	this.ui.bucketTree.SetCurrentItem(top)
 | 
			
		||||
 | 
			
		||||
	this.ui.bucketTree.ExpandItem(top)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) onactionExport_database_as_zip_triggered() {
 | 
			
		||||
 | 
			
		||||
	dbPath := qt.QFileDialog_GetOpenFileName2(this.Widget(), "Select bolt database...")
 | 
			
		||||
	if dbPath == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zipPath := qt.QFileDialog_GetSaveFileName4(this.Widget(), "Save as...", "", "Zip files (*.zip)")
 | 
			
		||||
	if zipPath == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		err := Bolt_ExportDatabaseToZip(dbPath, zipPath)
 | 
			
		||||
		mainthread.Start(func() {
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.alert(fmt.Sprintf("Error exporting database as zip: %s", err.Error()))
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			this.alert("Exported as zip successfully.")
 | 
			
		||||
		})
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) onactionCreate_database_from_zip_triggered() {
 | 
			
		||||
 | 
			
		||||
	zipPath := qt.QFileDialog_GetOpenFileName4(this.Widget(), "Select zip archive...", "", "Zip files (*.zip)")
 | 
			
		||||
	if zipPath == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbPath := qt.QFileDialog_GetSaveFileName2(this.Widget(), "Save as...")
 | 
			
		||||
	if dbPath == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Qt popped up a message saying 'will overwrite existing'
 | 
			
		||||
	// Make that true
 | 
			
		||||
	if err := os.Remove(dbPath); err != nil && !os.IsNotExist(err) {
 | 
			
		||||
		this.alert(fmt.Sprintf("Error removing existing database for overwrite: %s", err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		err := Bolt_ImportZipToDatabase(dbPath, zipPath)
 | 
			
		||||
		mainthread.Start(func() {
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.alert(fmt.Sprintf("Error importing database from zip %q: %s", zipPath, err.Error()))
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			this.alert("Imported zip to database successfully.")
 | 
			
		||||
		})
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) refreshBucketTree(itm *qt.QTreeWidgetItem) {
 | 
			
		||||
	ws := this.getSelection(itm)
 | 
			
		||||
 | 
			
		||||
	// Remove existing children
 | 
			
		||||
	i := itm.ChildCount()
 | 
			
		||||
	for i > 0 {
 | 
			
		||||
		itm.TakeChild(i - 1).Delete()
 | 
			
		||||
		i -= 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := Bolt_ListBuckets(ws.bdb, ws.browse, func(qba string) error {
 | 
			
		||||
 | 
			
		||||
		child := qt.NewQTreeWidgetItem6(itm) // NewQTreeWidgetItem()
 | 
			
		||||
		child.SetText(0, getDisplayName([]byte(qba)))
 | 
			
		||||
		child.SetData(0, BinaryDataRole, qt.NewQVariant12([]byte(qba)))
 | 
			
		||||
		child.SetIcon(0, qt.NewQIcon4(":/rsrc/table.png"))
 | 
			
		||||
 | 
			
		||||
		itm.AddChild(child)
 | 
			
		||||
		this.refreshBucketTree(child)
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.alert(fmt.Sprintf("Error listing buckets under %s: %s", strings.Join(ws.browse, `/`), err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionExit_triggered() {
 | 
			
		||||
	this.ui.MainWindow.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionAbout_Qt_triggered() {
 | 
			
		||||
	qt.QApplication_AboutQt()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionAbout_qbolt_triggered() {
 | 
			
		||||
	qt.QMessageBox_About(
 | 
			
		||||
		this.Widget(),
 | 
			
		||||
		qt.QGuiApplication_ApplicationDisplayName(),
 | 
			
		||||
		"<b>QBolt "+Version+"</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>",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionDisconnect_triggered() {
 | 
			
		||||
	top := this.lastContextSelection
 | 
			
		||||
	if top.Parent() != nil {
 | 
			
		||||
		return // somehow we didn't select a top-level item
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bdb := GET_BDB(top)
 | 
			
		||||
 | 
			
		||||
	// Remove UI
 | 
			
		||||
	this.ui.bucketTree.ClearSelection()
 | 
			
		||||
	top.Delete()
 | 
			
		||||
 | 
			
		||||
	// Disconnect from DB
 | 
			
		||||
	bdb.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_bucketTree_customContextMenuRequested(pos *qt.QPoint) {
 | 
			
		||||
 | 
			
		||||
	itm := this.ui.bucketTree.ItemAt(pos)
 | 
			
		||||
	if itm == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.lastContextSelection = itm
 | 
			
		||||
 | 
			
		||||
	if itm.Parent() != nil {
 | 
			
		||||
		// Child item, show the bucket menu
 | 
			
		||||
		this.bucketContext.Popup(this.ui.bucketTree.Viewport().MapToGlobalWithQPoint(pos))
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		// Top-level item, show the database menu
 | 
			
		||||
		this.databaseContext.Popup(this.ui.bucketTree.Viewport().MapToGlobalWithQPoint(pos))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionRefresh_buckets_triggered() {
 | 
			
		||||
	this.refreshBucketTree(this.lastContextSelection)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_bucketTree_currentItemChanged(current, previous *qt.QTreeWidgetItem) {
 | 
			
		||||
	_ = previous // Q_UNUSED
 | 
			
		||||
	if current == nil {
 | 
			
		||||
		this.ui.stackedWidget.SetVisible(false)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.ui.stackedWidget.SetVisible(true)
 | 
			
		||||
 | 
			
		||||
	if current.Parent() == nil {
 | 
			
		||||
		// Selected a database
 | 
			
		||||
		this.ui.stackedWidget.SetCurrentWidget(this.ui.databasePage)
 | 
			
		||||
		this.ui.databasePropertiesArea.Clear()
 | 
			
		||||
 | 
			
		||||
		bdb := GET_BDB(current)
 | 
			
		||||
		stats, err := Bolt_DBStats(bdb)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ui.databasePropertiesArea.SetPlainText(fmt.Sprintf("Error retrieving database statistics: %s", err.Error()))
 | 
			
		||||
		} else {
 | 
			
		||||
			this.ui.databasePropertiesArea.SetPlainText(stats)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Clean up foreign areas
 | 
			
		||||
		this.ui.bucketPropertiesArea.Clear()
 | 
			
		||||
		this.ui.bucketData.Clear()
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		// Selected a bucket
 | 
			
		||||
 | 
			
		||||
		this.ui.stackedWidget.SetCurrentWidget(this.ui.bucketPage)
 | 
			
		||||
		this.ui.bucketPropertiesArea.Clear()
 | 
			
		||||
 | 
			
		||||
		ws := this.getSelection(current)
 | 
			
		||||
 | 
			
		||||
		stats, err := Bolt_BucketStats(ws.bdb, ws.browse)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ui.bucketPropertiesArea.SetPlainText(fmt.Sprintf("Error retrieving bucket statistics: %s", err.Error()))
 | 
			
		||||
		} else {
 | 
			
		||||
			this.ui.bucketPropertiesArea.SetPlainText(stats)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Load the data tab
 | 
			
		||||
		this.refreshData(ws.bdb, ws.browse)
 | 
			
		||||
 | 
			
		||||
		// Clean up foreign areas
 | 
			
		||||
		this.ui.databasePropertiesArea.Clear()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) refreshData(bdb *bolt.DB, browse []string) {
 | 
			
		||||
	// Load the data tab
 | 
			
		||||
	this.ui.bucketData.Clear()
 | 
			
		||||
 | 
			
		||||
	err := Bolt_ListItems(bdb, browse, func(lii ListItemInfo) error {
 | 
			
		||||
		itm := qt.NewQTreeWidgetItem()
 | 
			
		||||
		itm.SetText(0, getDisplayName(lii.Name))
 | 
			
		||||
		itm.SetData(0, BinaryDataRole, qt.NewQVariant12([]byte(lii.Name)))
 | 
			
		||||
		itm.SetText(1, fmt.Sprintf("%d", lii.DataLen))
 | 
			
		||||
		this.ui.bucketData.AddTopLevelItem(itm)
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.alert(fmt.Sprintf("Error listing bucket content: %s", err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.ui.bucketData.ResizeColumnToContents(0)
 | 
			
		||||
	this.on_bucketData_itemSelectionChanged()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionClear_selection_triggered() {
 | 
			
		||||
	this.ui.bucketTree.SetCurrentItem(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type windowSelection struct {
 | 
			
		||||
	itm    *qt.QTreeWidgetItem
 | 
			
		||||
	top    *qt.QTreeWidgetItem
 | 
			
		||||
	browse []string
 | 
			
		||||
	bdb    *bolt.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) getWindowSelection() (windowSelection, bool) {
 | 
			
		||||
 | 
			
		||||
	itm := this.ui.bucketTree.CurrentItem()
 | 
			
		||||
	if itm == nil {
 | 
			
		||||
		return windowSelection{}, false // No selection
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return this.getSelection(itm), true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) getSelection(itm *qt.QTreeWidgetItem) windowSelection {
 | 
			
		||||
 | 
			
		||||
	top := itm
 | 
			
		||||
 | 
			
		||||
	var browse []string
 | 
			
		||||
	for {
 | 
			
		||||
		if top.Parent() == nil {
 | 
			
		||||
			break
 | 
			
		||||
		} else {
 | 
			
		||||
			browse = append(browse, string(top.Data(0, BinaryDataRole).ToByteArray()))
 | 
			
		||||
			top = top.Parent()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ReverseSlice(browse)
 | 
			
		||||
 | 
			
		||||
	bdb := GET_BDB(top)
 | 
			
		||||
 | 
			
		||||
	return windowSelection{itm: itm, top: top, browse: browse, bdb: bdb}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) openEditor(bdb *bolt.DB, saveAs []string, saveAsKey []byte, currentContent []byte) {
 | 
			
		||||
	iw := NewItemWindowUi()
 | 
			
		||||
 | 
			
		||||
	iw.contentArea.SetPlainText(string(currentContent))
 | 
			
		||||
	iw.ItemWindow.SetWindowTitle(getDisplayName(saveAsKey))
 | 
			
		||||
	iw.ItemWindow.SetWindowModality(qt.ApplicationModal) // we need this - otherwise we'll refresh a possibly-changed area after saving
 | 
			
		||||
	iw.ItemWindow.OnFinished(func(exitCode int) {
 | 
			
		||||
		if exitCode == int(qt.QDialog__Accepted) {
 | 
			
		||||
 | 
			
		||||
			err := Bolt_SetItem(bdb, saveAs, saveAsKey, []byte(iw.contentArea.ToPlainText()))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.alert(fmt.Sprintf("Error saving item content: %s", err.Error()))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			this.refreshData(bdb, saveAs)
 | 
			
		||||
		}
 | 
			
		||||
		iw.ItemWindow.DeleteLater()
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	iw.ItemWindow.Show()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_bucketData_doubleClicked(index *qt.QModelIndex) {
 | 
			
		||||
	ws, ok := this.getWindowSelection()
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return // no selection
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get item key
 | 
			
		||||
 | 
			
		||||
	key := index.DataWithRole(BinaryDataRole).ToByteArray()
 | 
			
		||||
 | 
			
		||||
	// DB lookup
 | 
			
		||||
	content, err := Bolt_GetItem(ws.bdb, ws.browse, key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.alert(fmt.Sprintf("Error loading item content: %s", err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.openEditor(ws.bdb, ws.browse, key, []byte(content))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionAdd_bucket_triggered() {
 | 
			
		||||
	ws, ok := this.getWindowSelection()
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return // no selection
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Prompt for bucket name
 | 
			
		||||
 | 
			
		||||
	name := qt.QInputDialog_GetText(this.Widget(), "New bucket", "Enter a key for the new bucket:")
 | 
			
		||||
	if len(name) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create
 | 
			
		||||
	err := Bolt_CreateBucket(ws.bdb, ws.browse, name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.alert(fmt.Sprintf("Error creating bucket: %s", err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Refresh bucket list
 | 
			
		||||
	this.refreshBucketTree(ws.itm) // sub-tree only
 | 
			
		||||
	this.ui.bucketTree.ExpandItem(ws.itm)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_actionDelete_bucket_triggered() {
 | 
			
		||||
	ws, ok := this.getWindowSelection()
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return // no selection
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Prompt for confirmation
 | 
			
		||||
	bucketToDelete := ws.itm.Data(0, BinaryDataRole).ToByteArray()
 | 
			
		||||
	if qt.QMessageBox_Question2(
 | 
			
		||||
		this.Widget(),
 | 
			
		||||
		"Delete bucket",
 | 
			
		||||
		fmt.Sprintf("Are you sure you want to remove the bucket '%s'?", getDisplayName(bucketToDelete)),
 | 
			
		||||
		qt.QMessageBox__Yes,
 | 
			
		||||
		qt.QMessageBox__Cancel,
 | 
			
		||||
	) != int(qt.QMessageBox__Yes) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parent := ws.itm.Parent()
 | 
			
		||||
 | 
			
		||||
	// One level down
 | 
			
		||||
 | 
			
		||||
	if len(ws.browse) > 0 {
 | 
			
		||||
		ws.browse = ws.browse[0 : len(ws.browse)-1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := Bolt_DeleteBucket(ws.bdb, ws.browse, string(bucketToDelete))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.alert(fmt.Sprintf("Error removing bucket: %s", err.Error()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Refresh bucket list
 | 
			
		||||
	this.refreshBucketTree(parent) // sub-tree only
 | 
			
		||||
	this.ui.bucketTree.ExpandItem(parent)
 | 
			
		||||
	this.ui.bucketTree.SetCurrentItem(parent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_AddDataButton_clicked() {
 | 
			
		||||
	ws, ok := this.getWindowSelection()
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return // no selection
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Prompt for bucket name
 | 
			
		||||
 | 
			
		||||
	name := qt.QInputDialog_GetText(this.Widget(), "New item", "Enter a key for the new item:")
 | 
			
		||||
	if len(name) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.openEditor(ws.bdb, ws.browse, []byte(name), []byte(""))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_DeleteDataButton_clicked() {
 | 
			
		||||
	ws, ok := this.getWindowSelection()
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return // no selection
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	selection := this.ui.bucketData.SelectedItems()
 | 
			
		||||
	if len(selection) == 0 {
 | 
			
		||||
		return // nothing to do
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Prompt for confirmation
 | 
			
		||||
	if qt.QMessageBox_Question2(this.Widget(), "Delete items", fmt.Sprintf("Are you sure you want to remove %d item(s)?", len(selection)), qt.QMessageBox__Yes, qt.QMessageBox__Cancel) != int(qt.QMessageBox__Yes) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var i int = len(selection)
 | 
			
		||||
	for i > 0 {
 | 
			
		||||
		err := Bolt_DeleteItem(ws.bdb, ws.browse, selection[i-1].Data(0, BinaryDataRole).ToByteArray())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.alert(fmt.Sprintf("Error removing item: %s", err.Error()))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		i -= 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.refreshData(ws.bdb, ws.browse)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MainWindow) on_bucketData_itemSelectionChanged() {
 | 
			
		||||
	this.ui.DeleteDataButton.SetEnabled(len(this.ui.bucketData.SelectedItems()) > 0)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										436
									
								
								mainwindow.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,436 @@
 | 
			
		||||
<?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>22</height>
 | 
			
		||||
    </rect>
 | 
			
		||||
   </property>
 | 
			
		||||
   <widget class="QMenu" name="menuFile">
 | 
			
		||||
    <property name="title">
 | 
			
		||||
     <string>&File</string>
 | 
			
		||||
    </property>
 | 
			
		||||
    <widget class="QMenu" name="menuConvert">
 | 
			
		||||
     <property name="title">
 | 
			
		||||
      <string>Convert</string>
 | 
			
		||||
     </property>
 | 
			
		||||
     <addaction name="actionExport_database_as_zip"/>
 | 
			
		||||
     <addaction name="actionCreate_database_from_zip"/>
 | 
			
		||||
    </widget>
 | 
			
		||||
    <addaction name="actionNew_database"/>
 | 
			
		||||
    <addaction name="actionOpen_database"/>
 | 
			
		||||
    <addaction name="actionOpen_database_as_read_only"/>
 | 
			
		||||
    <addaction name="separator"/>
 | 
			
		||||
    <addaction name="menuConvert"/>
 | 
			
		||||
    <addaction name="separator"/>
 | 
			
		||||
    <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>
 | 
			
		||||
   <property name="shortcut">
 | 
			
		||||
    <string>F1</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>
 | 
			
		||||
   <property name="shortcut">
 | 
			
		||||
    <string>F5</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>
 | 
			
		||||
  <action name="actionExport_database_as_zip">
 | 
			
		||||
   <property name="text">
 | 
			
		||||
    <string>Export database as zip</string>
 | 
			
		||||
   </property>
 | 
			
		||||
  </action>
 | 
			
		||||
  <action name="actionCreate_database_from_zip">
 | 
			
		||||
   <property name="text">
 | 
			
		||||
    <string>Create database from zip</string>
 | 
			
		||||
   </property>
 | 
			
		||||
  </action>
 | 
			
		||||
 </widget>
 | 
			
		||||
 <layoutdefault spacing="6" margin="11"/>
 | 
			
		||||
 <resources>
 | 
			
		||||
  <include location="resources.qrc"/>
 | 
			
		||||
 </resources>
 | 
			
		||||
 <connections/>
 | 
			
		||||
</ui>
 | 
			
		||||
							
								
								
									
										390
									
								
								mainwindow_ui.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,390 @@
 | 
			
		||||
// Generated by miqt-uic. To update this file, edit the .ui file in
 | 
			
		||||
// Qt Designer, and then run 'go generate'.
 | 
			
		||||
//
 | 
			
		||||
//go:generate miqt-uic -Qt6 -InFile mainwindow.ui -OutFile mainwindow_ui.go
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	qt "github.com/mappu/miqt/qt6"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MainWindowUi struct {
 | 
			
		||||
	MainWindow                       *qt.QMainWindow
 | 
			
		||||
	centralWidget                    *qt.QWidget
 | 
			
		||||
	gridLayout                       *qt.QGridLayout
 | 
			
		||||
	splitter                         *qt.QSplitter
 | 
			
		||||
	bucketTree                       *qt.QTreeWidget
 | 
			
		||||
	stackedWidget                    *qt.QStackedWidget
 | 
			
		||||
	databasePage                     *qt.QWidget
 | 
			
		||||
	gridLayout_4                     *qt.QGridLayout
 | 
			
		||||
	databaseTabWidget                *qt.QTabWidget
 | 
			
		||||
	databasePropertiesTab            *qt.QWidget
 | 
			
		||||
	gridLayout_2                     *qt.QGridLayout
 | 
			
		||||
	databasePropertiesArea           *qt.QPlainTextEdit
 | 
			
		||||
	bucketPage                       *qt.QWidget
 | 
			
		||||
	gridLayout_3                     *qt.QGridLayout
 | 
			
		||||
	bucketTabWidget                  *qt.QTabWidget
 | 
			
		||||
	bucketPropertiesTab              *qt.QWidget
 | 
			
		||||
	gridLayout_5                     *qt.QGridLayout
 | 
			
		||||
	bucketPropertiesArea             *qt.QPlainTextEdit
 | 
			
		||||
	bucketDataTab                    *qt.QWidget
 | 
			
		||||
	gridLayout_6                     *qt.QGridLayout
 | 
			
		||||
	bucketData                       *qt.QTreeWidget
 | 
			
		||||
	AddDataButton                    *qt.QPushButton
 | 
			
		||||
	horizontalSpacer                 *qt.QSpacerItem
 | 
			
		||||
	DeleteDataButton                 *qt.QPushButton
 | 
			
		||||
	menuBar                          *qt.QMenuBar
 | 
			
		||||
	menuFile                         *qt.QMenu
 | 
			
		||||
	menuConvert                      *qt.QMenu
 | 
			
		||||
	menuHelp                         *qt.QMenu
 | 
			
		||||
	menuView                         *qt.QMenu
 | 
			
		||||
	mainToolBar                      *qt.QToolBar
 | 
			
		||||
	statusBar                        *qt.QStatusBar
 | 
			
		||||
	actionAbout_qbolt                *qt.QAction
 | 
			
		||||
	actionAbout_Qt                   *qt.QAction
 | 
			
		||||
	actionOpen_database              *qt.QAction
 | 
			
		||||
	actionExit                       *qt.QAction
 | 
			
		||||
	actionDisconnect                 *qt.QAction
 | 
			
		||||
	actionDelete_bucket              *qt.QAction
 | 
			
		||||
	actionRefresh_buckets            *qt.QAction
 | 
			
		||||
	actionClear_selection            *qt.QAction
 | 
			
		||||
	actionNew_database               *qt.QAction
 | 
			
		||||
	actionAdd_bucket                 *qt.QAction
 | 
			
		||||
	actionOpen_database_as_read_only *qt.QAction
 | 
			
		||||
	actionExport_database_as_zip     *qt.QAction
 | 
			
		||||
	actionCreate_database_from_zip   *qt.QAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMainWindowUi creates all Qt widget classes for MainWindow.
 | 
			
		||||
func NewMainWindowUi() *MainWindowUi {
 | 
			
		||||
	ui := &MainWindowUi{}
 | 
			
		||||
	ui.MainWindow = qt.NewQMainWindow(nil)
 | 
			
		||||
	MainWindow__objectName := qt.NewQAnyStringView3("MainWindow")
 | 
			
		||||
	ui.MainWindow.SetObjectName(*MainWindow__objectName)
 | 
			
		||||
	MainWindow__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.MainWindow.Resize(668, 405)
 | 
			
		||||
	ui.MainWindow.SetWindowTitle("QBolt")
 | 
			
		||||
	icon0 := qt.NewQIcon()
 | 
			
		||||
	icon0.AddFile4(":/rsrc/database_lightning.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.MainWindow.SetWindowIcon(icon0)
 | 
			
		||||
	ui.actionAbout_qbolt = qt.NewQAction()
 | 
			
		||||
	actionAbout_qbolt__objectName := qt.NewQAnyStringView3("actionAbout_qbolt")
 | 
			
		||||
	ui.actionAbout_qbolt.SetObjectName(*actionAbout_qbolt__objectName)
 | 
			
		||||
	actionAbout_qbolt__objectName.Delete() // setter copied value
 | 
			
		||||
	icon1 := qt.NewQIcon()
 | 
			
		||||
	icon1.AddFile4(":/rsrc/information.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.actionAbout_qbolt.SetIcon(icon1)
 | 
			
		||||
	ui.actionAbout_Qt = qt.NewQAction()
 | 
			
		||||
	actionAbout_Qt__objectName := qt.NewQAnyStringView3("actionAbout_Qt")
 | 
			
		||||
	ui.actionAbout_Qt.SetObjectName(*actionAbout_Qt__objectName)
 | 
			
		||||
	actionAbout_Qt__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.actionOpen_database = qt.NewQAction()
 | 
			
		||||
	actionOpen_database__objectName := qt.NewQAnyStringView3("actionOpen_database")
 | 
			
		||||
	ui.actionOpen_database.SetObjectName(*actionOpen_database__objectName)
 | 
			
		||||
	actionOpen_database__objectName.Delete() // setter copied value
 | 
			
		||||
	icon2 := qt.NewQIcon()
 | 
			
		||||
	icon2.AddFile4(":/rsrc/database.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.actionOpen_database.SetIcon(icon2)
 | 
			
		||||
	ui.actionExit = qt.NewQAction()
 | 
			
		||||
	actionExit__objectName := qt.NewQAnyStringView3("actionExit")
 | 
			
		||||
	ui.actionExit.SetObjectName(*actionExit__objectName)
 | 
			
		||||
	actionExit__objectName.Delete() // setter copied value
 | 
			
		||||
	icon3 := qt.NewQIcon()
 | 
			
		||||
	icon3.AddFile4(":/rsrc/door_out.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.actionExit.SetIcon(icon3)
 | 
			
		||||
	ui.actionDisconnect = qt.NewQAction()
 | 
			
		||||
	actionDisconnect__objectName := qt.NewQAnyStringView3("actionDisconnect")
 | 
			
		||||
	ui.actionDisconnect.SetObjectName(*actionDisconnect__objectName)
 | 
			
		||||
	actionDisconnect__objectName.Delete() // setter copied value
 | 
			
		||||
	icon4 := qt.NewQIcon()
 | 
			
		||||
	icon4.AddFile4(":/rsrc/disconnect.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.actionDisconnect.SetIcon(icon4)
 | 
			
		||||
	ui.actionDelete_bucket = qt.NewQAction()
 | 
			
		||||
	actionDelete_bucket__objectName := qt.NewQAnyStringView3("actionDelete_bucket")
 | 
			
		||||
	ui.actionDelete_bucket.SetObjectName(*actionDelete_bucket__objectName)
 | 
			
		||||
	actionDelete_bucket__objectName.Delete() // setter copied value
 | 
			
		||||
	icon5 := qt.NewQIcon()
 | 
			
		||||
	icon5.AddFile4(":/rsrc/table_delete.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.actionDelete_bucket.SetIcon(icon5)
 | 
			
		||||
	ui.actionRefresh_buckets = qt.NewQAction()
 | 
			
		||||
	actionRefresh_buckets__objectName := qt.NewQAnyStringView3("actionRefresh_buckets")
 | 
			
		||||
	ui.actionRefresh_buckets.SetObjectName(*actionRefresh_buckets__objectName)
 | 
			
		||||
	actionRefresh_buckets__objectName.Delete() // setter copied value
 | 
			
		||||
	icon6 := qt.NewQIcon()
 | 
			
		||||
	icon6.AddFile4(":/rsrc/arrow_refresh.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.actionRefresh_buckets.SetIcon(icon6)
 | 
			
		||||
	ui.actionClear_selection = qt.NewQAction()
 | 
			
		||||
	actionClear_selection__objectName := qt.NewQAnyStringView3("actionClear_selection")
 | 
			
		||||
	ui.actionClear_selection.SetObjectName(*actionClear_selection__objectName)
 | 
			
		||||
	actionClear_selection__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.actionNew_database = qt.NewQAction()
 | 
			
		||||
	actionNew_database__objectName := qt.NewQAnyStringView3("actionNew_database")
 | 
			
		||||
	ui.actionNew_database.SetObjectName(*actionNew_database__objectName)
 | 
			
		||||
	actionNew_database__objectName.Delete() // setter copied value
 | 
			
		||||
	icon7 := qt.NewQIcon()
 | 
			
		||||
	icon7.AddFile4(":/rsrc/database_add.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.actionNew_database.SetIcon(icon7)
 | 
			
		||||
	ui.actionAdd_bucket = qt.NewQAction()
 | 
			
		||||
	actionAdd_bucket__objectName := qt.NewQAnyStringView3("actionAdd_bucket")
 | 
			
		||||
	ui.actionAdd_bucket.SetObjectName(*actionAdd_bucket__objectName)
 | 
			
		||||
	actionAdd_bucket__objectName.Delete() // setter copied value
 | 
			
		||||
	icon8 := qt.NewQIcon()
 | 
			
		||||
	icon8.AddFile4(":/rsrc/table_add.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.actionAdd_bucket.SetIcon(icon8)
 | 
			
		||||
	ui.actionOpen_database_as_read_only = qt.NewQAction()
 | 
			
		||||
	actionOpen_database_as_read_only__objectName := qt.NewQAnyStringView3("actionOpen_database_as_read_only")
 | 
			
		||||
	ui.actionOpen_database_as_read_only.SetObjectName(*actionOpen_database_as_read_only__objectName)
 | 
			
		||||
	actionOpen_database_as_read_only__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.actionExport_database_as_zip = qt.NewQAction()
 | 
			
		||||
	actionExport_database_as_zip__objectName := qt.NewQAnyStringView3("actionExport_database_as_zip")
 | 
			
		||||
	ui.actionExport_database_as_zip.SetObjectName(*actionExport_database_as_zip__objectName)
 | 
			
		||||
	actionExport_database_as_zip__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.actionCreate_database_from_zip = qt.NewQAction()
 | 
			
		||||
	actionCreate_database_from_zip__objectName := qt.NewQAnyStringView3("actionCreate_database_from_zip")
 | 
			
		||||
	ui.actionCreate_database_from_zip.SetObjectName(*actionCreate_database_from_zip__objectName)
 | 
			
		||||
	actionCreate_database_from_zip__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.centralWidget = qt.NewQWidget(ui.MainWindow.QWidget)
 | 
			
		||||
	centralWidget__objectName := qt.NewQAnyStringView3("centralWidget")
 | 
			
		||||
	ui.centralWidget.SetObjectName(*centralWidget__objectName)
 | 
			
		||||
	centralWidget__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout = qt.NewQGridLayout(ui.centralWidget)
 | 
			
		||||
	gridLayout__objectName := qt.NewQAnyStringView3("gridLayout")
 | 
			
		||||
	ui.gridLayout.SetObjectName(*gridLayout__objectName)
 | 
			
		||||
	gridLayout__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout.SetContentsMargins(0, 0, 0, 0)
 | 
			
		||||
	ui.gridLayout.SetSpacing(6)
 | 
			
		||||
	ui.splitter = qt.NewQSplitter(ui.centralWidget)
 | 
			
		||||
	splitter__objectName := qt.NewQAnyStringView3("splitter")
 | 
			
		||||
	ui.splitter.SetObjectName(*splitter__objectName)
 | 
			
		||||
	splitter__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.splitter.SetOrientation(qt.Horizontal)
 | 
			
		||||
	ui.splitter.SetChildrenCollapsible(false)
 | 
			
		||||
	ui.bucketTree = qt.NewQTreeWidget(ui.splitter.QWidget)
 | 
			
		||||
	bucketTree__objectName := qt.NewQAnyStringView3("bucketTree")
 | 
			
		||||
	ui.bucketTree.SetObjectName(*bucketTree__objectName)
 | 
			
		||||
	bucketTree__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.bucketTree.SetContextMenuPolicy(qt.CustomContextMenu)
 | 
			
		||||
	ui.bucketTree.SetUniformRowHeights(true)
 | 
			
		||||
	ui.splitter.AddWidget(ui.bucketTree.QWidget)
 | 
			
		||||
	ui.stackedWidget = qt.NewQStackedWidget(ui.splitter.QWidget)
 | 
			
		||||
	stackedWidget__objectName := qt.NewQAnyStringView3("stackedWidget")
 | 
			
		||||
	ui.stackedWidget.SetObjectName(*stackedWidget__objectName)
 | 
			
		||||
	stackedWidget__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.databasePage = qt.NewQWidget(ui.stackedWidget.QWidget)
 | 
			
		||||
	databasePage__objectName := qt.NewQAnyStringView3("databasePage")
 | 
			
		||||
	ui.databasePage.SetObjectName(*databasePage__objectName)
 | 
			
		||||
	databasePage__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_4 = qt.NewQGridLayout(ui.databasePage)
 | 
			
		||||
	gridLayout_4__objectName := qt.NewQAnyStringView3("gridLayout_4")
 | 
			
		||||
	ui.gridLayout_4.SetObjectName(*gridLayout_4__objectName)
 | 
			
		||||
	gridLayout_4__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_4.SetContentsMargins(0, 0, 0, 0)
 | 
			
		||||
	ui.gridLayout_4.SetSpacing(6)
 | 
			
		||||
	ui.databaseTabWidget = qt.NewQTabWidget(ui.databasePage)
 | 
			
		||||
	databaseTabWidget__objectName := qt.NewQAnyStringView3("databaseTabWidget")
 | 
			
		||||
	ui.databaseTabWidget.SetObjectName(*databaseTabWidget__objectName)
 | 
			
		||||
	databaseTabWidget__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.databasePropertiesTab = qt.NewQWidget(ui.databaseTabWidget.QWidget)
 | 
			
		||||
	databasePropertiesTab__objectName := qt.NewQAnyStringView3("databasePropertiesTab")
 | 
			
		||||
	ui.databasePropertiesTab.SetObjectName(*databasePropertiesTab__objectName)
 | 
			
		||||
	databasePropertiesTab__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_2 = qt.NewQGridLayout(ui.databasePropertiesTab)
 | 
			
		||||
	gridLayout_2__objectName := qt.NewQAnyStringView3("gridLayout_2")
 | 
			
		||||
	ui.gridLayout_2.SetObjectName(*gridLayout_2__objectName)
 | 
			
		||||
	gridLayout_2__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_2.SetContentsMargins(3, 3, 3, 3)
 | 
			
		||||
	ui.gridLayout_2.SetSpacing(6)
 | 
			
		||||
	ui.databasePropertiesArea = qt.NewQPlainTextEdit(ui.databasePropertiesTab)
 | 
			
		||||
	databasePropertiesArea__objectName := qt.NewQAnyStringView3("databasePropertiesArea")
 | 
			
		||||
	ui.databasePropertiesArea.SetObjectName(*databasePropertiesArea__objectName)
 | 
			
		||||
	databasePropertiesArea__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.databasePropertiesArea.SetFrameShape(qt.QFrame__NoFrame)
 | 
			
		||||
	ui.databasePropertiesArea.SetReadOnly(true)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_2.AddWidget2(ui.databasePropertiesArea.QWidget, 0, 0)
 | 
			
		||||
	icon9 := qt.NewQIcon()
 | 
			
		||||
	icon9.AddFile4(":/rsrc/chart_bar.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.databaseTabWidget.AddTab2(ui.databasePropertiesTab, icon9, "")
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_4.AddWidget2(ui.databaseTabWidget.QWidget, 0, 0)
 | 
			
		||||
	ui.stackedWidget.AddWidget(ui.databasePage)
 | 
			
		||||
	ui.bucketPage = qt.NewQWidget(ui.stackedWidget.QWidget)
 | 
			
		||||
	bucketPage__objectName := qt.NewQAnyStringView3("bucketPage")
 | 
			
		||||
	ui.bucketPage.SetObjectName(*bucketPage__objectName)
 | 
			
		||||
	bucketPage__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_3 = qt.NewQGridLayout(ui.bucketPage)
 | 
			
		||||
	gridLayout_3__objectName := qt.NewQAnyStringView3("gridLayout_3")
 | 
			
		||||
	ui.gridLayout_3.SetObjectName(*gridLayout_3__objectName)
 | 
			
		||||
	gridLayout_3__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_3.SetContentsMargins(0, 0, 0, 0)
 | 
			
		||||
	ui.gridLayout_3.SetSpacing(6)
 | 
			
		||||
	ui.bucketTabWidget = qt.NewQTabWidget(ui.bucketPage)
 | 
			
		||||
	bucketTabWidget__objectName := qt.NewQAnyStringView3("bucketTabWidget")
 | 
			
		||||
	ui.bucketTabWidget.SetObjectName(*bucketTabWidget__objectName)
 | 
			
		||||
	bucketTabWidget__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.bucketPropertiesTab = qt.NewQWidget(ui.bucketTabWidget.QWidget)
 | 
			
		||||
	bucketPropertiesTab__objectName := qt.NewQAnyStringView3("bucketPropertiesTab")
 | 
			
		||||
	ui.bucketPropertiesTab.SetObjectName(*bucketPropertiesTab__objectName)
 | 
			
		||||
	bucketPropertiesTab__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_5 = qt.NewQGridLayout(ui.bucketPropertiesTab)
 | 
			
		||||
	gridLayout_5__objectName := qt.NewQAnyStringView3("gridLayout_5")
 | 
			
		||||
	ui.gridLayout_5.SetObjectName(*gridLayout_5__objectName)
 | 
			
		||||
	gridLayout_5__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_5.SetContentsMargins(3, 3, 3, 3)
 | 
			
		||||
	ui.gridLayout_5.SetSpacing(6)
 | 
			
		||||
	ui.bucketPropertiesArea = qt.NewQPlainTextEdit(ui.bucketPropertiesTab)
 | 
			
		||||
	bucketPropertiesArea__objectName := qt.NewQAnyStringView3("bucketPropertiesArea")
 | 
			
		||||
	ui.bucketPropertiesArea.SetObjectName(*bucketPropertiesArea__objectName)
 | 
			
		||||
	bucketPropertiesArea__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.bucketPropertiesArea.SetFrameShape(qt.QFrame__NoFrame)
 | 
			
		||||
	ui.bucketPropertiesArea.SetReadOnly(true)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_5.AddWidget2(ui.bucketPropertiesArea.QWidget, 0, 0)
 | 
			
		||||
	icon10 := qt.NewQIcon()
 | 
			
		||||
	icon10.AddFile4(":/rsrc/chart_bar.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.bucketTabWidget.AddTab2(ui.bucketPropertiesTab, icon10, "")
 | 
			
		||||
	ui.bucketDataTab = qt.NewQWidget(ui.bucketTabWidget.QWidget)
 | 
			
		||||
	bucketDataTab__objectName := qt.NewQAnyStringView3("bucketDataTab")
 | 
			
		||||
	ui.bucketDataTab.SetObjectName(*bucketDataTab__objectName)
 | 
			
		||||
	bucketDataTab__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_6 = qt.NewQGridLayout(ui.bucketDataTab)
 | 
			
		||||
	gridLayout_6__objectName := qt.NewQAnyStringView3("gridLayout_6")
 | 
			
		||||
	ui.gridLayout_6.SetObjectName(*gridLayout_6__objectName)
 | 
			
		||||
	gridLayout_6__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.gridLayout_6.SetContentsMargins(3, 3, 3, 3)
 | 
			
		||||
	ui.gridLayout_6.SetSpacing(6)
 | 
			
		||||
	ui.bucketData = qt.NewQTreeWidget(ui.bucketDataTab)
 | 
			
		||||
	bucketData__objectName := qt.NewQAnyStringView3("bucketData")
 | 
			
		||||
	ui.bucketData.SetObjectName(*bucketData__objectName)
 | 
			
		||||
	bucketData__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.bucketData.SetSelectionMode(qt.QAbstractItemView__ExtendedSelection)
 | 
			
		||||
	ui.bucketData.SetIndentation(0)
 | 
			
		||||
	ui.bucketData.SetRootIsDecorated(false)
 | 
			
		||||
	ui.bucketData.SetUniformRowHeights(true)
 | 
			
		||||
	ui.bucketData.SetItemsExpandable(false)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_6.AddWidget3(ui.bucketData.QWidget, 0, 0, 1, 3)
 | 
			
		||||
	ui.AddDataButton = qt.NewQPushButton(ui.bucketDataTab)
 | 
			
		||||
	AddDataButton__objectName := qt.NewQAnyStringView3("AddDataButton")
 | 
			
		||||
	ui.AddDataButton.SetObjectName(*AddDataButton__objectName)
 | 
			
		||||
	AddDataButton__objectName.Delete() // setter copied value
 | 
			
		||||
	icon11 := qt.NewQIcon()
 | 
			
		||||
	icon11.AddFile4(":/rsrc/add.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.AddDataButton.SetIcon(icon11)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_6.AddWidget2(ui.AddDataButton.QWidget, 1, 0)
 | 
			
		||||
	/* miqt-uic: no handler for spacer */
 | 
			
		||||
	ui.DeleteDataButton = qt.NewQPushButton(ui.bucketDataTab)
 | 
			
		||||
	DeleteDataButton__objectName := qt.NewQAnyStringView3("DeleteDataButton")
 | 
			
		||||
	ui.DeleteDataButton.SetObjectName(*DeleteDataButton__objectName)
 | 
			
		||||
	DeleteDataButton__objectName.Delete() // setter copied value
 | 
			
		||||
	icon12 := qt.NewQIcon()
 | 
			
		||||
	icon12.AddFile4(":/rsrc/delete.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.DeleteDataButton.SetIcon(icon12)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_6.AddWidget2(ui.DeleteDataButton.QWidget, 1, 1)
 | 
			
		||||
	icon13 := qt.NewQIcon()
 | 
			
		||||
	icon13.AddFile4(":/rsrc/table.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
 | 
			
		||||
	ui.bucketTabWidget.AddTab2(ui.bucketDataTab, icon13, "")
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout_3.AddWidget2(ui.bucketTabWidget.QWidget, 0, 0)
 | 
			
		||||
	ui.stackedWidget.AddWidget(ui.bucketPage)
 | 
			
		||||
	ui.splitter.AddWidget(ui.stackedWidget.QWidget)
 | 
			
		||||
 | 
			
		||||
	ui.gridLayout.AddWidget2(ui.splitter.QWidget, 0, 0)
 | 
			
		||||
	ui.MainWindow.SetCentralWidget(ui.centralWidget) // Set central widget
 | 
			
		||||
	ui.menuBar = qt.NewQMenuBar(ui.MainWindow.QWidget)
 | 
			
		||||
	menuBar__objectName := qt.NewQAnyStringView3("menuBar")
 | 
			
		||||
	ui.menuBar.SetObjectName(*menuBar__objectName)
 | 
			
		||||
	menuBar__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.menuBar.Resize(668, 22)
 | 
			
		||||
	ui.menuFile = qt.NewQMenu(ui.menuBar.QWidget)
 | 
			
		||||
	menuFile__objectName := qt.NewQAnyStringView3("menuFile")
 | 
			
		||||
	ui.menuFile.SetObjectName(*menuFile__objectName)
 | 
			
		||||
	menuFile__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.menuConvert = qt.NewQMenu(ui.menuFile.QWidget)
 | 
			
		||||
	menuConvert__objectName := qt.NewQAnyStringView3("menuConvert")
 | 
			
		||||
	ui.menuConvert.SetObjectName(*menuConvert__objectName)
 | 
			
		||||
	menuConvert__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.menuConvert.QWidget.AddAction(ui.actionExport_database_as_zip)
 | 
			
		||||
	ui.menuConvert.QWidget.AddAction(ui.actionCreate_database_from_zip)
 | 
			
		||||
	ui.menuFile.QWidget.AddAction(ui.actionNew_database)
 | 
			
		||||
	ui.menuFile.QWidget.AddAction(ui.actionOpen_database)
 | 
			
		||||
	ui.menuFile.QWidget.AddAction(ui.actionOpen_database_as_read_only)
 | 
			
		||||
	ui.menuFile.AddSeparator()
 | 
			
		||||
	ui.menuFile.AddMenu(ui.menuConvert)
 | 
			
		||||
	ui.menuFile.AddSeparator()
 | 
			
		||||
	ui.menuFile.AddSeparator()
 | 
			
		||||
	ui.menuFile.QWidget.AddAction(ui.actionExit)
 | 
			
		||||
	ui.menuHelp = qt.NewQMenu(ui.menuBar.QWidget)
 | 
			
		||||
	menuHelp__objectName := qt.NewQAnyStringView3("menuHelp")
 | 
			
		||||
	ui.menuHelp.SetObjectName(*menuHelp__objectName)
 | 
			
		||||
	menuHelp__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.menuHelp.QWidget.AddAction(ui.actionAbout_qbolt)
 | 
			
		||||
	ui.menuHelp.QWidget.AddAction(ui.actionAbout_Qt)
 | 
			
		||||
	ui.menuView = qt.NewQMenu(ui.menuBar.QWidget)
 | 
			
		||||
	menuView__objectName := qt.NewQAnyStringView3("menuView")
 | 
			
		||||
	ui.menuView.SetObjectName(*menuView__objectName)
 | 
			
		||||
	menuView__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.menuView.QWidget.AddAction(ui.actionClear_selection)
 | 
			
		||||
	ui.menuBar.AddMenu(ui.menuFile)
 | 
			
		||||
	ui.menuBar.AddMenu(ui.menuView)
 | 
			
		||||
	ui.menuBar.AddMenu(ui.menuHelp)
 | 
			
		||||
	ui.MainWindow.SetMenuBar(ui.menuBar)
 | 
			
		||||
	ui.mainToolBar = qt.NewQToolBar(ui.MainWindow.QWidget)
 | 
			
		||||
	mainToolBar__objectName := qt.NewQAnyStringView3("mainToolBar")
 | 
			
		||||
	ui.mainToolBar.SetObjectName(*mainToolBar__objectName)
 | 
			
		||||
	mainToolBar__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.MainWindow.AddToolBar(qt.TopToolBarArea, ui.mainToolBar)
 | 
			
		||||
	/* miqt-uic: no handler for mainToolBar attribute 'toolBarBreak' */
 | 
			
		||||
	ui.mainToolBar.QWidget.AddAction(ui.actionNew_database)
 | 
			
		||||
	ui.mainToolBar.QWidget.AddAction(ui.actionOpen_database)
 | 
			
		||||
	ui.mainToolBar.AddSeparator()
 | 
			
		||||
	ui.statusBar = qt.NewQStatusBar(ui.MainWindow.QWidget)
 | 
			
		||||
	statusBar__objectName := qt.NewQAnyStringView3("statusBar")
 | 
			
		||||
	ui.statusBar.SetObjectName(*statusBar__objectName)
 | 
			
		||||
	statusBar__objectName.Delete() // setter copied value
 | 
			
		||||
	ui.MainWindow.SetStatusBar(ui.statusBar)
 | 
			
		||||
 | 
			
		||||
	ui.Retranslate()
 | 
			
		||||
 | 
			
		||||
	ui.stackedWidget.SetCurrentIndex(0)
 | 
			
		||||
	ui.databaseTabWidget.SetCurrentIndex(0)
 | 
			
		||||
	ui.bucketTabWidget.SetCurrentIndex(0)
 | 
			
		||||
 | 
			
		||||
	return ui
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Retranslate reapplies all text translations.
 | 
			
		||||
func (ui *MainWindowUi) Retranslate() {
 | 
			
		||||
	ui.actionAbout_qbolt.SetText(qt.QMainWindow_Tr("&About QBolt"))
 | 
			
		||||
	ui.actionAbout_qbolt.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("F1")))
 | 
			
		||||
	ui.actionAbout_Qt.SetText(qt.QMainWindow_Tr("About &Qt"))
 | 
			
		||||
	ui.actionOpen_database.SetText(qt.QMainWindow_Tr("&Open database..."))
 | 
			
		||||
	ui.actionOpen_database.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("Ctrl+O")))
 | 
			
		||||
	ui.actionExit.SetText(qt.QMainWindow_Tr("&Exit"))
 | 
			
		||||
	ui.actionDisconnect.SetText(qt.QMainWindow_Tr("Disconnect"))
 | 
			
		||||
	ui.actionDelete_bucket.SetText(qt.QMainWindow_Tr("Delete bucket"))
 | 
			
		||||
	ui.actionRefresh_buckets.SetText(qt.QMainWindow_Tr("Refresh buckets"))
 | 
			
		||||
	ui.actionRefresh_buckets.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("F5")))
 | 
			
		||||
	ui.actionClear_selection.SetText(qt.QMainWindow_Tr("&Clear selection"))
 | 
			
		||||
	ui.actionNew_database.SetText(qt.QMainWindow_Tr("&New database..."))
 | 
			
		||||
	ui.actionAdd_bucket.SetText(qt.QMainWindow_Tr("Add bucket..."))
 | 
			
		||||
	ui.actionOpen_database_as_read_only.SetText(qt.QMainWindow_Tr("Open database as read-only..."))
 | 
			
		||||
	ui.actionExport_database_as_zip.SetText(qt.QMainWindow_Tr("Export database as zip"))
 | 
			
		||||
	ui.actionCreate_database_from_zip.SetText(qt.QMainWindow_Tr("Create database from zip"))
 | 
			
		||||
	ui.bucketTree.HeaderItem().SetText(0, qt.QTreeWidget_Tr("Bucket"))
 | 
			
		||||
	ui.databaseTabWidget.SetTabText(ui.databaseTabWidget.IndexOf(ui.databasePropertiesTab), qt.QTabWidget_Tr("Database"))
 | 
			
		||||
	ui.databasePropertiesArea.SetPlainText(qt.QWidget_Tr("No selection"))
 | 
			
		||||
	ui.bucketTabWidget.SetTabText(ui.bucketTabWidget.IndexOf(ui.bucketPropertiesTab), qt.QTabWidget_Tr("Bucket"))
 | 
			
		||||
	ui.bucketTabWidget.SetTabText(ui.bucketTabWidget.IndexOf(ui.bucketDataTab), qt.QTabWidget_Tr("Data"))
 | 
			
		||||
	ui.bucketData.HeaderItem().SetText(0, qt.QTreeWidget_Tr("Key"))
 | 
			
		||||
	ui.bucketData.HeaderItem().SetText(1, qt.QTreeWidget_Tr("Data length"))
 | 
			
		||||
	ui.AddDataButton.SetText(qt.QWidget_Tr("Add..."))
 | 
			
		||||
	ui.DeleteDataButton.SetText(qt.QWidget_Tr("Delete..."))
 | 
			
		||||
	ui.menuFile.SetTitle(qt.QMenuBar_Tr("&File"))
 | 
			
		||||
	ui.menuConvert.SetTitle(qt.QMenu_Tr("Convert"))
 | 
			
		||||
	ui.menuHelp.SetTitle(qt.QMenuBar_Tr("Help"))
 | 
			
		||||
	ui.menuView.SetTitle(qt.QMenuBar_Tr("&View"))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								resources.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,17 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
//go:generate miqt-rcc -Input "resources.qrc" -OutputGo "resources.go" -OutputRcc "resources.rcc" -Qt6
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"embed"
 | 
			
		||||
 | 
			
		||||
	qt "github.com/mappu/miqt/qt6"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//go:embed resources.rcc
 | 
			
		||||
var _resourceRcc []byte
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	_ = embed.FS{}
 | 
			
		||||
	qt.QResource_RegisterResourceWithRccData(&_resourceRcc[0])
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								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
										
									
								
								resources.rcc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								rsrc/add.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 733 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/arrow_refresh.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 685 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/chart_bar.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 541 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/database.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 390 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/database_add.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 658 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/database_connect.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 763 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/database_lightning.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 775 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/delete.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 715 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/disconnect.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 796 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/door_out.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 688 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/information.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 778 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/page.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 635 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/qbolt.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/table.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 566 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/table_add.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 663 B  | 
							
								
								
									
										
											BIN
										
									
								
								rsrc/table_delete.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 660 B  | 
							
								
								
									
										40
									
								
								util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,40 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getDisplayName(qba []byte) string {
 | 
			
		||||
	if len(qba) ==0 {
 | 
			
		||||
		return "<empty>"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret := strconv.Quote(string(qba))
 | 
			
		||||
	return ret[1 : len(ret)-1]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReverseSlice reverses a slice.
 | 
			
		||||
// @ref https://stackoverflow.com/a/28058324
 | 
			
		||||
func ReverseSlice[S ~[]E, E any](s S)  {
 | 
			
		||||
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
 | 
			
		||||
        s[i], s[j] = s[j], s[i]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CopySliceAdd copies a slice and adds one more thing on the end.
 | 
			
		||||
func CopySliceAdd[T comparable](base []T, and T) []T {	
 | 
			
		||||
	ret := make([]T, len(base)+1)
 | 
			
		||||
	copy(ret, base)
 | 
			
		||||
	ret[len(ret)-1] = and
 | 
			
		||||
	
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Apply creates a new slice where every element of the input arr is transformed.
 | 
			
		||||
func Apply[T comparable](arr []T, transform func(T) T) []T {
 | 
			
		||||
	ret := make([]T, len(arr))
 | 
			
		||||
	for i := 0; i < len(arr); i++ {
 | 
			
		||||
		ret[i] = transform(arr[i])
 | 
			
		||||
	}
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								util_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,17 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestQByteArray(t *testing.T) {
 | 
			
		||||
	foo := "the \x00 quick \x01 brown \x02 fox \x03 jumps \x04 over \x05 the \x06 lazy \x07 dog"
 | 
			
		||||
 | 
			
		||||
	qb := MakeQByteArray(foo)
 | 
			
		||||
 | 
			
		||||
	out := FromQByteArray(qb)
 | 
			
		||||
 | 
			
		||||
	if foo != out {
 | 
			
		||||
		t.Errorf("Expected equal")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								windows-manifest.template.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,50 @@
 | 
			
		||||
{
 | 
			
		||||
  "RT_GROUP_ICON": {
 | 
			
		||||
    "APP": {
 | 
			
		||||
      "0000": "rsrc/database_lightning.png"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "RT_VERSION": {
 | 
			
		||||
    "#1": {
 | 
			
		||||
      "0000": {
 | 
			
		||||
        "fixed": {
 | 
			
		||||
          "file_version": "%VERSION%.0",
 | 
			
		||||
          "product_version": "%VERSION%.0"
 | 
			
		||||
        },
 | 
			
		||||
        "info": {
 | 
			
		||||
          "0409": {
 | 
			
		||||
            "FileDescription": "QBolt Database Viewer",
 | 
			
		||||
            "FileVersion": "%VERSION%.0",
 | 
			
		||||
            "ProductName": "QBolt Database Viewer",
 | 
			
		||||
            "ProductVersion": "%VERSION%.0"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "RT_MANIFEST": {
 | 
			
		||||
    "#1": {
 | 
			
		||||
      "0409": {
 | 
			
		||||
        "identity": {
 | 
			
		||||
          "name": "",
 | 
			
		||||
          "version": ""
 | 
			
		||||
        },
 | 
			
		||||
        "description": "",
 | 
			
		||||
        "minimum-os": "win7",
 | 
			
		||||
        "execution-level": "as invoker",
 | 
			
		||||
        "ui-access": false,
 | 
			
		||||
        "auto-elevate": false,
 | 
			
		||||
        "dpi-awareness": "per monitor v2",
 | 
			
		||||
        "disable-theming": false,
 | 
			
		||||
        "disable-window-filtering": false,
 | 
			
		||||
        "high-resolution-scrolling-aware": false,
 | 
			
		||||
        "ultra-high-resolution-scrolling-aware": false,
 | 
			
		||||
        "long-path-aware": false,
 | 
			
		||||
        "printer-driver-isolation": false,
 | 
			
		||||
        "gdi-scaling": false,
 | 
			
		||||
        "segment-heap": false,
 | 
			
		||||
        "use-common-controls-v6": true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||