diff --git a/MemoryStore.go b/MemoryStore.go
deleted file mode 100644
index 95ffbf3..0000000
--- a/MemoryStore.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package main
-
-import "C"
-import (
- "errors"
- "sync"
-)
-
-type ObjectReference int64
-
-var NullObjectReference error = errors.New("Null object reference")
-
-// GoMemoryStore is a int->interface storage structure so that Go pointers are
-// never exposed to C code.
-type GoMemoryStore struct {
- mtx sync.RWMutex
- items map[int64]interface{}
- next int64
-}
-
-func NewGoMemoryStore() *GoMemoryStore {
- ret := GoMemoryStore{}
- ret.items = make(map[int64]interface{})
- return &ret
-}
-
-func (this *GoMemoryStore) Put(itm interface{}) ObjectReference {
- this.mtx.Lock()
- defer this.mtx.Unlock()
-
- key := this.next
- this.items[key] = itm
- this.next++
- return ObjectReference(key)
-}
-
-func (this *GoMemoryStore) Get(i ObjectReference) (interface{}, bool) {
- this.mtx.RLock()
- defer this.mtx.RUnlock()
-
- ret, ok := this.items[int64(i)]
- return ret, ok
-}
-
-func (this *GoMemoryStore) Delete(i ObjectReference) {
- this.mtx.Lock()
- defer this.mtx.Unlock()
-
- delete(this.items, int64(i))
-}
-
-var gms *GoMemoryStore = nil
-
-func init() {
- gms = NewGoMemoryStore()
-}
diff --git a/bolt.go b/bolt.go
new file mode 100644
index 0000000..44c19df
--- /dev/null
+++ b/bolt.go
@@ -0,0 +1,219 @@
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "os"
+ "time"
+
+ bolt "github.com/boltdb/bolt"
+)
+
+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 string) 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([]byte(key), []byte(val))
+ })
+}
+
+func Bolt_DeleteItem(db *bolt.DB, browse []string, key string) 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([]byte(key))
+ })
+}
+
+func Bolt_DBStats(db *bolt.DB) (string, error) {
+ jBytes, err := json.Marshal(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.Marshal(stats)
+ if err != nil {
+ return "", err
+ }
+
+ return string(jBytes), err
+}
+
+func Bolt_ListBuckets(db *bolt.DB, browse []string, cb func(b string)) error {
+
+ if len(browse) == 0 {
+ // root mode
+ return db.View(func(tx *bolt.Tx) error {
+ return tx.ForEach(func(k []byte, _ *bolt.Bucket) error {
+ cb(string(k))
+ return nil
+ })
+ })
+
+ }
+
+ // 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 {
+ cb(string(k))
+ }
+ return nil
+ })
+ })
+}
+
+type ListItemInfo struct {
+ Name string
+ 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
+ }
+
+ return cb(ListItemInfo{string(k), int64(len(v))})
+ })
+ })
+}
+
+func Bolt_GetItem(db *bolt.DB, browse []string, key string) (string, error) {
+ var ret string
+
+ err := withBrowse_ReadOnly(db, browse, func(tx *bolt.Tx, bucket *bolt.Bucket) error {
+ d := bucket.Get([]byte(key))
+ ret = string(d)
+ return nil
+ })
+ return ret, err
+}
+
+func Bolt_Close(db *bolt.DB) error {
+ return db.Close()
+}
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..5d07b70
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -eu
+
+MIQT_UIC=${MIQT_UIC:-miqt-uic}
+
+rm resources.rcc || true
+rcc --binary -o resources.rcc resources.qrc
+
+$MIQT_UIC -InFile mainwindow.ui -OutFile mainwindow_ui.go
+$MIQT_UIC -InFile itemwindow.ui -OutFile itemwindow_ui.go
+
+go build -ldflags "-s -w"
+
+echo "Build OK"
diff --git a/docker/win32-cross-qt-mxe.Dockerfile b/docker/win32-cross-qt-mxe.Dockerfile
deleted file mode 100644
index c50e9e4..0000000
--- a/docker/win32-cross-qt-mxe.Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-FROM debian:bullseye
-
-RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
- apt-get install -qyy gnupg2 golang-go ca-certificates
-
-RUN DEBIAN_FRONTEND=noninteractive \
- echo "deb https://pkg.mxe.cc/repos/apt buster main" >/etc/apt/sources.list.d/mxeapt.list && \
- apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 86B72ED9 && \
- apt-get update && \
- apt-get install -qyy mxe-i686-w64-mingw32.static-qt5 && \
- apt-get clean
-
-ENV PATH=/usr/lib/mxe/usr/bin:$PATH
diff --git a/go.mod b/go.mod
index b16dcfb..c61c52d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,10 @@
module code.ivysaur.me/qbolt
-go 1.15
+go 1.19
-require github.com/boltdb/bolt v1.3.1
+require (
+ github.com/boltdb/bolt v1.3.1
+ github.com/mappu/miqt v0.5.0
+)
replace github.com/boltdb/bolt => go.etcd.io/bbolt v1.3.5
diff --git a/go.sum b/go.sum
index 218565d..bb9a7fb 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/mappu/miqt v0.5.0 h1:BWajkNI9PWlWN6ZDgWKwv1gieBGEImRqlWS8ZqDmDfA=
+github.com/mappu/miqt v0.5.0/go.mod h1:xFg7ADaO1QSkmXPsPODoKe/bydJpRG9fgCYyIDl/h1U=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/qbolt/itemwindow.ui b/itemwindow.ui
similarity index 100%
rename from qbolt/itemwindow.ui
rename to itemwindow.ui
diff --git a/itemwindow_ui.go b/itemwindow_ui.go
new file mode 100644
index 0000000..ba2c93e
--- /dev/null
+++ b/itemwindow_ui.go
@@ -0,0 +1,71 @@
+// Generated by miqt-uic. To update this file, edit the .ui file in
+// Qt Designer, and then run 'go generate'.
+//
+//go:generate miqt-uic -InFile itemwindow.ui -OutFile itemwindow_ui.go
+
+package main
+
+import (
+ "github.com/mappu/miqt/qt"
+)
+
+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.NewQDialog2(nil)
+ ui.ItemWindow.SetObjectName("ItemWindow")
+ 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)
+ ui.gridLayout_2.SetObjectName("gridLayout_2")
+ ui.gridLayout_2.SetVerticalSpacing(0)
+ ui.gridLayout_2.SetContentsMargins(0, 0, 0, 0)
+ ui.gridLayout_2.SetSpacing(6)
+
+ ui.contentArea = qt.NewQPlainTextEdit3(ui.ItemWindow.QWidget)
+ ui.contentArea.SetObjectName("contentArea")
+ ui.contentArea.SetFrameShape(qt.QFrame__NoFrame)
+
+ ui.gridLayout_2.AddWidget2(ui.contentArea.QWidget, 0, 0)
+
+ ui.frame = qt.NewQFrame2(ui.ItemWindow.QWidget)
+ ui.frame.SetObjectName("frame")
+ ui.frame.SetFrameShape(qt.QFrame__StyledPanel)
+ ui.frame.SetFrameShadow(qt.QFrame__Raised)
+
+ ui.gridLayout = qt.NewQGridLayout(ui.frame.QWidget)
+ ui.gridLayout.SetObjectName("gridLayout")
+ ui.gridLayout.SetContentsMargins(11, 11, 11, 11)
+ ui.gridLayout.SetSpacing(6)
+
+ ui.buttonBox = qt.NewQDialogButtonBox5(ui.frame.QWidget)
+ ui.buttonBox.SetObjectName("buttonBox")
+ /* miqt-uic: no handler for buttonBox property 'standardButtons' */
+
+ 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() {
+
+}
diff --git a/main.go b/main.go
index 09fdd65..d3e22bf 100644
--- a/main.go
+++ b/main.go
@@ -1,398 +1,20 @@
package main
-import "C"
import (
- "encoding/binary"
- "encoding/json"
- "errors"
"os"
- "time"
- bolt "github.com/boltdb/bolt"
+ "github.com/mappu/miqt/qt"
)
-const (
- ERROR_AND_STOP_CALLING int64 = 100
- ERROR_AND_KEEP_CALLING = 101
- FINISHED_OK = 102
- REAL_MESSAGE = 103
-)
-
-const Magic int64 = 0x10203040
-
-//export GetMagic
-func GetMagic() int64 {
- return Magic
-}
-
-//export Bolt_Open
-func Bolt_Open(readOnly bool, path string) (ObjectReference, *C.char, int) {
- opts := *bolt.DefaultOptions
- opts.Timeout = 10 * time.Second
- opts.ReadOnly = readOnly
-
- ptrDB, err := bolt.Open(path, os.FileMode(0644), &opts)
- if err != nil {
- errMsg := err.Error()
- return 0, C.CString(errMsg), len(errMsg)
- }
-
- dbRef := gms.Put(ptrDB)
- return dbRef, nil, 0
-}
-
-func withBoltDBReference(b ObjectReference, fn func(db *bolt.DB) error) error {
- dbIFC, ok := gms.Get(b)
- if !ok {
- return NullObjectReference
- }
-
- ptrDB, ok := dbIFC.(*bolt.DB)
- if !ok {
- return NullObjectReference
- }
-
- return fn(ptrDB)
-}
-
-func walkBuckets(tx *bolt.Tx, browse []string) (*bolt.Bucket, error) {
- bucket := tx.Bucket([]byte(browse[0]))
- if bucket == nil {
- return nil, errors.New("Unknown bucket")
- }
-
- for i := 1; i < len(browse); i += 1 {
- bucket = bucket.Bucket([]byte(browse[i]))
- if bucket == nil {
- return nil, errors.New("Unknown bucket")
- }
- }
-
- return bucket, nil
-}
-
-func withBrowse_ReadOnly(b_ref ObjectReference, browse []string, fn func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error) error {
- if len(browse) == 0 {
- // not a bucket
- return errors.New("No bucket selected")
- }
-
- return withBoltDBReference(b_ref, func(db *bolt.DB) error {
- return db.View(func(tx *bolt.Tx) error {
-
- bucket, err := walkBuckets(tx, browse)
- if err != nil {
- return err
- }
-
- // Walked the bucket chain, now run the user callback
- return fn(db, tx, bucket)
-
- })
- })
-}
-
-func err2triple(err error) (int64, *C.char, int) {
- if err != nil {
- msg := err.Error()
- return ERROR_AND_STOP_CALLING, C.CString(msg), len(msg)
- }
-
- return FINISHED_OK, nil, 0
-}
-
-//export Bolt_CreateBucket
-func Bolt_CreateBucket(b_ref ObjectReference, browse []string, newBucket string) (int64, *C.char, int) {
- err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
- return db.Update(func(tx *bolt.Tx) error {
-
- if len(browse) == 0 {
- // Top-level bucket
- _, err := tx.CreateBucket([]byte(newBucket))
- return err
-
- } else {
- // Deeper bucket
- bucket, err := walkBuckets(tx, browse)
- if err != nil {
- return err
- }
-
- // Walked the bucket chain, now create the new bucket
- _, err = bucket.CreateBucket([]byte(newBucket))
- return err
- }
- })
- })
-
- return err2triple(err)
-}
-
-//export Bolt_DeleteBucket
-func Bolt_DeleteBucket(b_ref ObjectReference, browse []string, delBucket string) (int64, *C.char, int) {
- err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
- return db.Update(func(tx *bolt.Tx) error {
-
- if len(browse) == 0 {
- // Top-level bucket
- return tx.DeleteBucket([]byte(delBucket))
-
- } else {
- // Deeper bucket
- bucket, err := walkBuckets(tx, browse)
- if err != nil {
- return err
- }
-
- // Walked the bucket chain, now delete the selected bucket
- return bucket.DeleteBucket([]byte(delBucket))
- }
- })
- })
-
- return err2triple(err)
-}
-
-//export Bolt_SetItem
-func Bolt_SetItem(b_ref ObjectReference, browse []string, key, val string) (int64, *C.char, int) {
- if len(browse) == 0 {
- return err2triple(errors.New("Can't create top-level items"))
- }
-
- err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
- return db.Update(func(tx *bolt.Tx) error {
-
- bucket, err := walkBuckets(tx, browse)
- if err != nil {
- return err
- }
-
- return bucket.Put([]byte(key), []byte(val))
- })
- })
-
- return err2triple(err)
-}
-
-//export Bolt_DeleteItem
-func Bolt_DeleteItem(b_ref ObjectReference, browse []string, key string) (int64, *C.char, int) {
- if len(browse) == 0 {
- return err2triple(errors.New("Can't create top-level items"))
- }
-
- err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
- return db.Update(func(tx *bolt.Tx) error {
-
- bucket, err := walkBuckets(tx, browse)
- if err != nil {
- return err
- }
-
- return bucket.Delete([]byte(key))
- })
- })
-
- return err2triple(err)
-}
-
-type CallResponse struct {
- s string
- e error
-}
-
-//export Bolt_DBStats
-func Bolt_DBStats(b ObjectReference) (int64, *C.char, int) {
- var stats bolt.Stats
-
- err := withBoltDBReference(b, func(db *bolt.DB) error {
- stats = db.Stats()
- return nil
- })
-
- jBytes, err := json.Marshal(stats)
- if err != nil {
- return err2triple(err)
- }
-
- return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes)
-}
-
-//export Bolt_BucketStats
-func Bolt_BucketStats(b ObjectReference, browse []string) (int64, *C.char, int) {
- var stats bolt.BucketStats
-
- err := withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error {
- stats = bucket.Stats()
- return nil
- })
-
- if err != nil {
- return err2triple(err)
- }
-
- jBytes, err := json.Marshal(stats)
- if err != nil {
- return err2triple(err)
- }
-
- return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes)
-}
-
-type NextCall struct {
- content chan CallResponse
-}
-
-//export Bolt_ListBuckets
-func Bolt_ListBuckets(b ObjectReference, browse []string) ObjectReference {
- pNC := &NextCall{
- content: make(chan CallResponse, 0),
- }
-
- pNC_Ref := gms.Put(pNC)
-
- go func() {
- var err error
-
- if len(browse) == 0 {
- // root mode
- err = withBoltDBReference(b, func(db *bolt.DB) error {
- return db.View(func(tx *bolt.Tx) error {
- return tx.ForEach(func(k []byte, _ *bolt.Bucket) error {
- pNC.content <- CallResponse{s: string(k)}
- return nil
- })
- })
- })
-
- } else {
- // Nested-mode
- err = withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error {
- return bucket.ForEach(func(k, v []byte) error {
- // non-nil v means it's a data item
- if v == nil {
- pNC.content <- CallResponse{s: string(k)}
- }
- return nil
- })
- })
-
- }
-
- if err != nil {
- pNC.content <- CallResponse{e: err}
- }
-
- close(pNC.content)
- }()
-
- return pNC_Ref
-}
-
-//export Bolt_ListItems
-func Bolt_ListItems(b ObjectReference, browse []string) ObjectReference {
-
- pNC := &NextCall{
- content: make(chan CallResponse, 0),
- }
-
- pNC_Ref := gms.Put(pNC)
-
- go func() {
- var err error
-
- if len(browse) == 0 {
- err = errors.New("No bucket specified")
- } else {
- // Nested-mode
- err = withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error {
- return bucket.ForEach(func(k, v []byte) error {
- if v == nil {
- return nil // nil v means it's a bucket, skip
- }
-
- itemLength := make([]byte, 8)
- binary.LittleEndian.PutUint64(itemLength, uint64(len(v)))
- pNC.content <- CallResponse{s: string(itemLength) + string(k)}
- return nil
- })
- })
- }
-
- if err != nil {
- pNC.content <- CallResponse{e: err}
- }
-
- close(pNC.content)
- }()
-
- return pNC_Ref
-}
-
-//export Bolt_GetItem
-func Bolt_GetItem(b ObjectReference, browse []string, key string) (int64, *C.char, int) {
- var ret *C.char = nil
- var ret_len = 0
-
- err := withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error {
- d := bucket.Get([]byte(key))
- ret = C.CString(string(d))
- ret_len = len(d)
- return nil
- })
-
- if err != nil {
- return err2triple(err)
- }
-
- return REAL_MESSAGE, ret, ret_len
-}
-
-//export GetNext
-func GetNext(oRef ObjectReference) (int64, *C.char, int) {
- pNC_Iface, ok := gms.Get(oRef)
- if !ok {
- return err2triple(NullObjectReference)
- }
-
- pNC, ok := pNC_Iface.(*NextCall)
- if !ok {
- return err2triple(NullObjectReference)
- }
-
- cr, ok := <-pNC.content
- if !ok {
- gms.Delete(oRef)
- return err2triple(nil)
- }
-
- if cr.e != nil {
- msg := cr.e.Error()
- return ERROR_AND_KEEP_CALLING, C.CString(msg), len(msg)
- }
-
- return REAL_MESSAGE, C.CString(cr.s), len(cr.s)
-}
-
-//export Bolt_ListBucketsAtRoot
-func Bolt_ListBucketsAtRoot(b ObjectReference) ObjectReference {
- return Bolt_ListBuckets(b, nil)
-}
-
-//export Bolt_Close
-func Bolt_Close(b ObjectReference) (*C.char, int) {
- err := withBoltDBReference(b, func(db *bolt.DB) error {
- return db.Close()
- })
-
- if err != nil {
- msg := err.Error()
- return C.CString(msg), len(msg)
- }
-
- gms.Delete(b)
- return nil, 0
-}
-
func main() {
- // virtual
+
+ _ = qt.NewQApplication(os.Args)
+
+ qt.QGuiApplication_SetApplicationDisplayName("QBolt")
+ qt.QGuiApplication_SetWindowIcon(qt.NewQIcon4(":/rsrc/database_lightning.png"))
+
+ w := NewMainWindow()
+ w.ui.MainWindow.Show()
+
+ qt.QApplication_Exec()
}
diff --git a/mainwindow.go b/mainwindow.go
new file mode 100644
index 0000000..c401ac7
--- /dev/null
+++ b/mainwindow.go
@@ -0,0 +1,509 @@
+package main
+
+import (
+ "fmt"
+ "strconv"
+
+ bolt "github.com/boltdb/bolt"
+ "github.com/mappu/miqt/qt"
+)
+
+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.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.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.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)
+
+ 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.NewQVariant7(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) 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, 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 getDisplayName(qba string) string {
+ ret := strconv.Quote(qba)
+ return ret[1 : len(ret)-2]
+}
+
+func (this *MainWindow) refreshBucketTree(itm *qt.QTreeWidgetItem) {
+ var top *qt.QTreeWidgetItem = itm
+
+ var browsePath []string
+ for {
+ browsePath = append(browsePath, top.Data(0, BinaryDataRole).ToString())
+ if top.Parent() == nil {
+ break
+ } else {
+ top = top.Parent()
+ }
+ }
+
+ ReverseSlice(browsePath)
+
+ // Remove existing children
+ i := itm.ChildCount()
+ for i > 0 {
+ itm.TakeChild(i).Delete()
+ i -= 1
+ }
+
+ bdb := GET_BDB(top)
+
+ err := Bolt_ListBuckets(bdb, browsePath, func(qba string) {
+ child := qt.NewQTreeWidgetItem()
+ child.SetText(0, getDisplayName(qba))
+ child.SetData(0, BinaryDataRole, qt.NewQVariant14(qba))
+ child.SetIcon(0, qt.NewQIcon4(":/rsrc/table.png"))
+
+ itm.AddChild(child)
+ this.refreshBucketTree(child)
+ })
+ if err != nil {
+ this.alert(fmt.Sprintf("Error listing buckets: %s", 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(),
+ "QBolt
Graphical interface for managing Bolt databases
"+
+ "- About BoltDB
"+
+ "- FamFamFam "Silk" icon set
"+
+ "- QBolt homepage
",
+ )
+}
+
+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.MapToGlobal(pos))
+
+ } else {
+ // Top-level item, show the database menu
+ this.databaseContext.Popup(this.ui.bucketTree.MapToGlobal(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()
+
+ top := current
+ var browse []string
+ for {
+ browse = append(browse, top.Data(0, BinaryDataRole).ToString())
+ if top.Parent() == nil {
+ break
+ } else {
+ top = top.Parent()
+ }
+ }
+ ReverseSlice(browse)
+ bdb := GET_BDB(top)
+
+ stats, err := Bolt_BucketStats(bdb, browse)
+ if err != nil {
+ this.ui.databasePropertiesArea.SetPlainText(fmt.Sprintf("Error retrieving bucket statistics: %s", err.Error()))
+ } else {
+ this.ui.databasePropertiesArea.SetPlainText(stats)
+ }
+
+ // Load the data tab
+ this.refreshData(bdb, 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.NewQVariant14(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
+ }
+
+ top := itm
+
+ var browse []string
+ for {
+ browse = append(browse, top.Data(0, BinaryDataRole).ToString())
+ if top.Parent() == nil {
+ break
+ } else {
+ top = top.Parent()
+ }
+ }
+
+ ReverseSlice(browse)
+
+ bdb := GET_BDB(top)
+
+ return windowSelection{itm: itm, top: top, browse: browse, bdb: bdb}, true
+}
+
+func (this *MainWindow) openEditor(bdb *bolt.DB, saveAs []string, saveAsKey string, 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, 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
+
+ model := index.Model()
+ key := model.Data2(model.Index(index.Row(), 0), BinaryDataRole).ToString()
+
+ // 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).ToString()
+ if qt.QMessageBox_Question4(
+ 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, 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, 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_Question4(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].Data(0, BinaryDataRole).ToString())
+ 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)
+}
diff --git a/qbolt/mainwindow.ui b/mainwindow.ui
similarity index 100%
rename from qbolt/mainwindow.ui
rename to mainwindow.ui
diff --git a/mainwindow_ui.cpp.txt b/mainwindow_ui.cpp.txt
new file mode 100644
index 0000000..9a177c0
--- /dev/null
+++ b/mainwindow_ui.cpp.txt
@@ -0,0 +1,336 @@
+/********************************************************************************
+** Form generated from reading UI file 'mainwindow.ui'
+**
+** Created by: Qt User Interface Compiler version 5.15.8
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef MAINWINDOW_UI_H
+#define MAINWINDOW_UI_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+
+class Ui_MainWindow
+{
+public:
+ QAction *actionAbout_qbolt;
+ QAction *actionAbout_Qt;
+ QAction *actionOpen_database;
+ QAction *actionExit;
+ QAction *actionDisconnect;
+ QAction *actionDelete_bucket;
+ QAction *actionRefresh_buckets;
+ QAction *actionClear_selection;
+ QAction *actionNew_database;
+ QAction *actionAdd_bucket;
+ QAction *actionOpen_database_as_read_only;
+ QWidget *centralWidget;
+ QGridLayout *gridLayout;
+ QSplitter *splitter;
+ QTreeWidget *bucketTree;
+ QStackedWidget *stackedWidget;
+ QWidget *databasePage;
+ QGridLayout *gridLayout_4;
+ QTabWidget *databaseTabWidget;
+ QWidget *databasePropertiesTab;
+ QGridLayout *gridLayout_2;
+ QPlainTextEdit *databasePropertiesArea;
+ QWidget *bucketPage;
+ QGridLayout *gridLayout_3;
+ QTabWidget *bucketTabWidget;
+ QWidget *bucketPropertiesTab;
+ QGridLayout *gridLayout_5;
+ QPlainTextEdit *bucketPropertiesArea;
+ QWidget *bucketDataTab;
+ QGridLayout *gridLayout_6;
+ QTreeWidget *bucketData;
+ QPushButton *AddDataButton;
+ QSpacerItem *horizontalSpacer;
+ QPushButton *DeleteDataButton;
+ QMenuBar *menuBar;
+ QMenu *menuFile;
+ QMenu *menuHelp;
+ QMenu *menuView;
+ QToolBar *mainToolBar;
+ QStatusBar *statusBar;
+
+ void setupUi(QMainWindow *MainWindow)
+ {
+ if (MainWindow->objectName().isEmpty())
+ MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
+ MainWindow->resize(668, 405);
+ QIcon icon;
+ icon.addFile(QString::fromUtf8(":/rsrc/database_lightning.png"), QSize(), QIcon::Normal, QIcon::Off);
+ MainWindow->setWindowIcon(icon);
+ actionAbout_qbolt = new QAction(MainWindow);
+ actionAbout_qbolt->setObjectName(QString::fromUtf8("actionAbout_qbolt"));
+ QIcon icon1;
+ icon1.addFile(QString::fromUtf8(":/rsrc/information.png"), QSize(), QIcon::Normal, QIcon::Off);
+ actionAbout_qbolt->setIcon(icon1);
+ actionAbout_Qt = new QAction(MainWindow);
+ actionAbout_Qt->setObjectName(QString::fromUtf8("actionAbout_Qt"));
+ actionOpen_database = new QAction(MainWindow);
+ actionOpen_database->setObjectName(QString::fromUtf8("actionOpen_database"));
+ QIcon icon2;
+ icon2.addFile(QString::fromUtf8(":/rsrc/database.png"), QSize(), QIcon::Normal, QIcon::Off);
+ actionOpen_database->setIcon(icon2);
+ actionExit = new QAction(MainWindow);
+ actionExit->setObjectName(QString::fromUtf8("actionExit"));
+ QIcon icon3;
+ icon3.addFile(QString::fromUtf8(":/rsrc/door_out.png"), QSize(), QIcon::Normal, QIcon::Off);
+ actionExit->setIcon(icon3);
+ actionDisconnect = new QAction(MainWindow);
+ actionDisconnect->setObjectName(QString::fromUtf8("actionDisconnect"));
+ QIcon icon4;
+ icon4.addFile(QString::fromUtf8(":/rsrc/disconnect.png"), QSize(), QIcon::Normal, QIcon::Off);
+ actionDisconnect->setIcon(icon4);
+ actionDelete_bucket = new QAction(MainWindow);
+ actionDelete_bucket->setObjectName(QString::fromUtf8("actionDelete_bucket"));
+ QIcon icon5;
+ icon5.addFile(QString::fromUtf8(":/rsrc/table_delete.png"), QSize(), QIcon::Normal, QIcon::Off);
+ actionDelete_bucket->setIcon(icon5);
+ actionRefresh_buckets = new QAction(MainWindow);
+ actionRefresh_buckets->setObjectName(QString::fromUtf8("actionRefresh_buckets"));
+ QIcon icon6;
+ icon6.addFile(QString::fromUtf8(":/rsrc/arrow_refresh.png"), QSize(), QIcon::Normal, QIcon::Off);
+ actionRefresh_buckets->setIcon(icon6);
+ actionClear_selection = new QAction(MainWindow);
+ actionClear_selection->setObjectName(QString::fromUtf8("actionClear_selection"));
+ actionNew_database = new QAction(MainWindow);
+ actionNew_database->setObjectName(QString::fromUtf8("actionNew_database"));
+ QIcon icon7;
+ icon7.addFile(QString::fromUtf8(":/rsrc/database_add.png"), QSize(), QIcon::Normal, QIcon::Off);
+ actionNew_database->setIcon(icon7);
+ actionAdd_bucket = new QAction(MainWindow);
+ actionAdd_bucket->setObjectName(QString::fromUtf8("actionAdd_bucket"));
+ QIcon icon8;
+ icon8.addFile(QString::fromUtf8(":/rsrc/table_add.png"), QSize(), QIcon::Normal, QIcon::Off);
+ actionAdd_bucket->setIcon(icon8);
+ actionOpen_database_as_read_only = new QAction(MainWindow);
+ actionOpen_database_as_read_only->setObjectName(QString::fromUtf8("actionOpen_database_as_read_only"));
+ centralWidget = new QWidget(MainWindow);
+ centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
+ gridLayout = new QGridLayout(centralWidget);
+ gridLayout->setSpacing(6);
+ gridLayout->setContentsMargins(11, 11, 11, 11);
+ gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
+ gridLayout->setContentsMargins(0, 0, 0, 0);
+ splitter = new QSplitter(centralWidget);
+ splitter->setObjectName(QString::fromUtf8("splitter"));
+ splitter->setOrientation(Qt::Horizontal);
+ splitter->setChildrenCollapsible(false);
+ bucketTree = new QTreeWidget(splitter);
+ bucketTree->setObjectName(QString::fromUtf8("bucketTree"));
+ bucketTree->setContextMenuPolicy(Qt::CustomContextMenu);
+ bucketTree->setUniformRowHeights(true);
+ splitter->addWidget(bucketTree);
+ stackedWidget = new QStackedWidget(splitter);
+ stackedWidget->setObjectName(QString::fromUtf8("stackedWidget"));
+ databasePage = new QWidget();
+ databasePage->setObjectName(QString::fromUtf8("databasePage"));
+ gridLayout_4 = new QGridLayout(databasePage);
+ gridLayout_4->setSpacing(6);
+ gridLayout_4->setContentsMargins(11, 11, 11, 11);
+ gridLayout_4->setObjectName(QString::fromUtf8("gridLayout_4"));
+ gridLayout_4->setContentsMargins(0, 0, 0, 0);
+ databaseTabWidget = new QTabWidget(databasePage);
+ databaseTabWidget->setObjectName(QString::fromUtf8("databaseTabWidget"));
+ databasePropertiesTab = new QWidget();
+ databasePropertiesTab->setObjectName(QString::fromUtf8("databasePropertiesTab"));
+ gridLayout_2 = new QGridLayout(databasePropertiesTab);
+ gridLayout_2->setSpacing(6);
+ gridLayout_2->setContentsMargins(11, 11, 11, 11);
+ gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2"));
+ gridLayout_2->setContentsMargins(3, 3, 3, 3);
+ databasePropertiesArea = new QPlainTextEdit(databasePropertiesTab);
+ databasePropertiesArea->setObjectName(QString::fromUtf8("databasePropertiesArea"));
+ databasePropertiesArea->setFrameShape(QFrame::NoFrame);
+ databasePropertiesArea->setReadOnly(true);
+
+ gridLayout_2->addWidget(databasePropertiesArea, 0, 0, 1, 1);
+
+ QIcon icon9;
+ icon9.addFile(QString::fromUtf8(":/rsrc/chart_bar.png"), QSize(), QIcon::Normal, QIcon::Off);
+ databaseTabWidget->addTab(databasePropertiesTab, icon9, QString());
+
+ gridLayout_4->addWidget(databaseTabWidget, 0, 0, 1, 1);
+
+ stackedWidget->addWidget(databasePage);
+ bucketPage = new QWidget();
+ bucketPage->setObjectName(QString::fromUtf8("bucketPage"));
+ gridLayout_3 = new QGridLayout(bucketPage);
+ gridLayout_3->setSpacing(6);
+ gridLayout_3->setContentsMargins(11, 11, 11, 11);
+ gridLayout_3->setObjectName(QString::fromUtf8("gridLayout_3"));
+ gridLayout_3->setContentsMargins(0, 0, 0, 0);
+ bucketTabWidget = new QTabWidget(bucketPage);
+ bucketTabWidget->setObjectName(QString::fromUtf8("bucketTabWidget"));
+ bucketPropertiesTab = new QWidget();
+ bucketPropertiesTab->setObjectName(QString::fromUtf8("bucketPropertiesTab"));
+ gridLayout_5 = new QGridLayout(bucketPropertiesTab);
+ gridLayout_5->setSpacing(6);
+ gridLayout_5->setContentsMargins(11, 11, 11, 11);
+ gridLayout_5->setObjectName(QString::fromUtf8("gridLayout_5"));
+ gridLayout_5->setContentsMargins(3, 3, 3, 3);
+ bucketPropertiesArea = new QPlainTextEdit(bucketPropertiesTab);
+ bucketPropertiesArea->setObjectName(QString::fromUtf8("bucketPropertiesArea"));
+ bucketPropertiesArea->setFrameShape(QFrame::NoFrame);
+ bucketPropertiesArea->setReadOnly(true);
+
+ gridLayout_5->addWidget(bucketPropertiesArea, 0, 0, 1, 1);
+
+ bucketTabWidget->addTab(bucketPropertiesTab, icon9, QString());
+ bucketDataTab = new QWidget();
+ bucketDataTab->setObjectName(QString::fromUtf8("bucketDataTab"));
+ gridLayout_6 = new QGridLayout(bucketDataTab);
+ gridLayout_6->setSpacing(6);
+ gridLayout_6->setContentsMargins(11, 11, 11, 11);
+ gridLayout_6->setObjectName(QString::fromUtf8("gridLayout_6"));
+ gridLayout_6->setContentsMargins(3, 3, 3, 3);
+ bucketData = new QTreeWidget(bucketDataTab);
+ bucketData->setObjectName(QString::fromUtf8("bucketData"));
+ bucketData->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ bucketData->setIndentation(0);
+ bucketData->setRootIsDecorated(false);
+ bucketData->setUniformRowHeights(true);
+ bucketData->setItemsExpandable(false);
+
+ gridLayout_6->addWidget(bucketData, 0, 0, 1, 3);
+
+ AddDataButton = new QPushButton(bucketDataTab);
+ AddDataButton->setObjectName(QString::fromUtf8("AddDataButton"));
+ QIcon icon10;
+ icon10.addFile(QString::fromUtf8(":/rsrc/add.png"), QSize(), QIcon::Normal, QIcon::Off);
+ AddDataButton->setIcon(icon10);
+
+ gridLayout_6->addWidget(AddDataButton, 1, 0, 1, 1);
+
+ horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+
+ gridLayout_6->addItem(horizontalSpacer, 1, 2, 1, 1);
+
+ DeleteDataButton = new QPushButton(bucketDataTab);
+ DeleteDataButton->setObjectName(QString::fromUtf8("DeleteDataButton"));
+ QIcon icon11;
+ icon11.addFile(QString::fromUtf8(":/rsrc/delete.png"), QSize(), QIcon::Normal, QIcon::Off);
+ DeleteDataButton->setIcon(icon11);
+
+ gridLayout_6->addWidget(DeleteDataButton, 1, 1, 1, 1);
+
+ QIcon icon12;
+ icon12.addFile(QString::fromUtf8(":/rsrc/table.png"), QSize(), QIcon::Normal, QIcon::Off);
+ bucketTabWidget->addTab(bucketDataTab, icon12, QString());
+
+ gridLayout_3->addWidget(bucketTabWidget, 0, 0, 1, 1);
+
+ stackedWidget->addWidget(bucketPage);
+ splitter->addWidget(stackedWidget);
+
+ gridLayout->addWidget(splitter, 0, 0, 1, 1);
+
+ MainWindow->setCentralWidget(centralWidget);
+ menuBar = new QMenuBar(MainWindow);
+ menuBar->setObjectName(QString::fromUtf8("menuBar"));
+ menuBar->setGeometry(QRect(0, 0, 668, 21));
+ menuFile = new QMenu(menuBar);
+ menuFile->setObjectName(QString::fromUtf8("menuFile"));
+ menuHelp = new QMenu(menuBar);
+ menuHelp->setObjectName(QString::fromUtf8("menuHelp"));
+ menuView = new QMenu(menuBar);
+ menuView->setObjectName(QString::fromUtf8("menuView"));
+ MainWindow->setMenuBar(menuBar);
+ mainToolBar = new QToolBar(MainWindow);
+ mainToolBar->setObjectName(QString::fromUtf8("mainToolBar"));
+ MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
+ statusBar = new QStatusBar(MainWindow);
+ statusBar->setObjectName(QString::fromUtf8("statusBar"));
+ MainWindow->setStatusBar(statusBar);
+
+ menuBar->addAction(menuFile->menuAction());
+ menuBar->addAction(menuView->menuAction());
+ menuBar->addAction(menuHelp->menuAction());
+ menuFile->addAction(actionNew_database);
+ menuFile->addAction(actionOpen_database);
+ menuFile->addAction(actionOpen_database_as_read_only);
+ menuFile->addSeparator();
+ menuFile->addAction(actionExit);
+ menuHelp->addAction(actionAbout_qbolt);
+ menuHelp->addAction(actionAbout_Qt);
+ menuView->addAction(actionClear_selection);
+ mainToolBar->addAction(actionNew_database);
+ mainToolBar->addAction(actionOpen_database);
+ mainToolBar->addSeparator();
+
+ retranslateUi(MainWindow);
+
+ stackedWidget->setCurrentIndex(0);
+ databaseTabWidget->setCurrentIndex(0);
+ bucketTabWidget->setCurrentIndex(0);
+
+
+ QMetaObject::connectSlotsByName(MainWindow);
+ } // setupUi
+
+ void retranslateUi(QMainWindow *MainWindow)
+ {
+ MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "QBolt", nullptr));
+ actionAbout_qbolt->setText(QCoreApplication::translate("MainWindow", "&About QBolt", nullptr));
+ actionAbout_Qt->setText(QCoreApplication::translate("MainWindow", "About &Qt", nullptr));
+ actionOpen_database->setText(QCoreApplication::translate("MainWindow", "&Open database...", nullptr));
+#if QT_CONFIG(shortcut)
+ actionOpen_database->setShortcut(QCoreApplication::translate("MainWindow", "Ctrl+O", nullptr));
+#endif // QT_CONFIG(shortcut)
+ actionExit->setText(QCoreApplication::translate("MainWindow", "&Exit", nullptr));
+ actionDisconnect->setText(QCoreApplication::translate("MainWindow", "Disconnect", nullptr));
+ actionDelete_bucket->setText(QCoreApplication::translate("MainWindow", "Delete bucket", nullptr));
+ actionRefresh_buckets->setText(QCoreApplication::translate("MainWindow", "Refresh buckets", nullptr));
+ actionClear_selection->setText(QCoreApplication::translate("MainWindow", "&Clear selection", nullptr));
+ actionNew_database->setText(QCoreApplication::translate("MainWindow", "&New database...", nullptr));
+ actionAdd_bucket->setText(QCoreApplication::translate("MainWindow", "Add bucket...", nullptr));
+ actionOpen_database_as_read_only->setText(QCoreApplication::translate("MainWindow", "Open database as read-only...", nullptr));
+ QTreeWidgetItem *___qtreewidgetitem = bucketTree->headerItem();
+ ___qtreewidgetitem->setText(0, QCoreApplication::translate("MainWindow", "Bucket", nullptr));
+ databasePropertiesArea->setPlainText(QCoreApplication::translate("MainWindow", "No selection", nullptr));
+ databaseTabWidget->setTabText(databaseTabWidget->indexOf(databasePropertiesTab), QCoreApplication::translate("MainWindow", "Database", nullptr));
+ bucketTabWidget->setTabText(bucketTabWidget->indexOf(bucketPropertiesTab), QCoreApplication::translate("MainWindow", "Bucket", nullptr));
+ QTreeWidgetItem *___qtreewidgetitem1 = bucketData->headerItem();
+ ___qtreewidgetitem1->setText(1, QCoreApplication::translate("MainWindow", "Data length", nullptr));
+ ___qtreewidgetitem1->setText(0, QCoreApplication::translate("MainWindow", "Key", nullptr));
+ AddDataButton->setText(QCoreApplication::translate("MainWindow", "Add...", nullptr));
+ DeleteDataButton->setText(QCoreApplication::translate("MainWindow", "Delete...", nullptr));
+ bucketTabWidget->setTabText(bucketTabWidget->indexOf(bucketDataTab), QCoreApplication::translate("MainWindow", "Data", nullptr));
+ menuFile->setTitle(QCoreApplication::translate("MainWindow", "Fi&le", nullptr));
+ menuHelp->setTitle(QCoreApplication::translate("MainWindow", "Help", nullptr));
+ menuView->setTitle(QCoreApplication::translate("MainWindow", "&View", nullptr));
+ } // retranslateUi
+
+};
+
+namespace Ui {
+ class MainWindow: public Ui_MainWindow {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // MAINWINDOW_UI_H
diff --git a/mainwindow_ui.go b/mainwindow_ui.go
new file mode 100644
index 0000000..f387c4f
--- /dev/null
+++ b/mainwindow_ui.go
@@ -0,0 +1,325 @@
+// Generated by miqt-uic. To update this file, edit the .ui file in
+// Qt Designer, and then run 'go generate'.
+//
+//go:generate miqt-uic -InFile mainwindow.ui -OutFile mainwindow_ui.go
+
+package main
+
+import (
+ "github.com/mappu/miqt/qt"
+)
+
+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
+ 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
+}
+
+// NewMainWindowUi creates all Qt widget classes for MainWindow.
+func NewMainWindowUi() *MainWindowUi {
+ ui := &MainWindowUi{}
+
+ ui.MainWindow = qt.NewQMainWindow2(nil)
+ ui.MainWindow.SetObjectName("MainWindow")
+ 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()
+ ui.actionAbout_qbolt.SetObjectName("actionAbout_qbolt")
+ 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()
+ ui.actionAbout_Qt.SetObjectName("actionAbout_Qt")
+
+ ui.actionOpen_database = qt.NewQAction()
+ ui.actionOpen_database.SetObjectName("actionOpen_database")
+ 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()
+ ui.actionExit.SetObjectName("actionExit")
+ 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()
+ ui.actionDisconnect.SetObjectName("actionDisconnect")
+ 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()
+ ui.actionDelete_bucket.SetObjectName("actionDelete_bucket")
+ 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()
+ ui.actionRefresh_buckets.SetObjectName("actionRefresh_buckets")
+ 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()
+ ui.actionClear_selection.SetObjectName("actionClear_selection")
+
+ ui.actionNew_database = qt.NewQAction()
+ ui.actionNew_database.SetObjectName("actionNew_database")
+ 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()
+ ui.actionAdd_bucket.SetObjectName("actionAdd_bucket")
+ 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()
+ ui.actionOpen_database_as_read_only.SetObjectName("actionOpen_database_as_read_only")
+
+ ui.centralWidget = qt.NewQWidget2(ui.MainWindow.QWidget)
+ ui.centralWidget.SetObjectName("centralWidget")
+
+ ui.gridLayout = qt.NewQGridLayout(ui.centralWidget)
+ ui.gridLayout.SetObjectName("gridLayout")
+ ui.gridLayout.SetContentsMargins(0, 0, 0, 0)
+ ui.gridLayout.SetSpacing(6)
+
+ ui.splitter = qt.NewQSplitter3(ui.centralWidget)
+ ui.splitter.SetObjectName("splitter")
+ ui.splitter.SetOrientation(qt.Horizontal)
+ ui.splitter.SetChildrenCollapsible(false)
+
+ ui.bucketTree = qt.NewQTreeWidget2(ui.splitter.QWidget)
+ ui.bucketTree.SetObjectName("bucketTree")
+ ui.bucketTree.SetContextMenuPolicy(qt.CustomContextMenu)
+ ui.bucketTree.SetUniformRowHeights(true)
+ ui.splitter.AddWidget(ui.bucketTree.QWidget)
+
+ ui.stackedWidget = qt.NewQStackedWidget2(ui.splitter.QWidget)
+ ui.stackedWidget.SetObjectName("stackedWidget")
+
+ ui.databasePage = qt.NewQWidget2(ui.stackedWidget.QWidget)
+ ui.databasePage.SetObjectName("databasePage")
+
+ ui.gridLayout_4 = qt.NewQGridLayout(ui.databasePage)
+ ui.gridLayout_4.SetObjectName("gridLayout_4")
+ ui.gridLayout_4.SetContentsMargins(0, 0, 0, 0)
+ ui.gridLayout_4.SetSpacing(6)
+
+ ui.databaseTabWidget = qt.NewQTabWidget2(ui.databasePage)
+ ui.databaseTabWidget.SetObjectName("databaseTabWidget")
+
+ ui.databasePropertiesTab = qt.NewQWidget2(ui.databaseTabWidget.QWidget)
+ ui.databasePropertiesTab.SetObjectName("databasePropertiesTab")
+
+ ui.gridLayout_2 = qt.NewQGridLayout(ui.databasePropertiesTab)
+ ui.gridLayout_2.SetObjectName("gridLayout_2")
+ ui.gridLayout_2.SetContentsMargins(3, 3, 3, 3)
+ ui.gridLayout_2.SetSpacing(6)
+
+ ui.databasePropertiesArea = qt.NewQPlainTextEdit3(ui.databasePropertiesTab)
+ ui.databasePropertiesArea.SetObjectName("databasePropertiesArea")
+ 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.NewQWidget2(ui.stackedWidget.QWidget)
+ ui.bucketPage.SetObjectName("bucketPage")
+
+ ui.gridLayout_3 = qt.NewQGridLayout(ui.bucketPage)
+ ui.gridLayout_3.SetObjectName("gridLayout_3")
+ ui.gridLayout_3.SetContentsMargins(0, 0, 0, 0)
+ ui.gridLayout_3.SetSpacing(6)
+
+ ui.bucketTabWidget = qt.NewQTabWidget2(ui.bucketPage)
+ ui.bucketTabWidget.SetObjectName("bucketTabWidget")
+
+ ui.bucketPropertiesTab = qt.NewQWidget2(ui.bucketTabWidget.QWidget)
+ ui.bucketPropertiesTab.SetObjectName("bucketPropertiesTab")
+
+ ui.gridLayout_5 = qt.NewQGridLayout(ui.bucketPropertiesTab)
+ ui.gridLayout_5.SetObjectName("gridLayout_5")
+ ui.gridLayout_5.SetContentsMargins(3, 3, 3, 3)
+ ui.gridLayout_5.SetSpacing(6)
+
+ ui.bucketPropertiesArea = qt.NewQPlainTextEdit3(ui.bucketPropertiesTab)
+ ui.bucketPropertiesArea.SetObjectName("bucketPropertiesArea")
+ 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.NewQWidget2(ui.bucketTabWidget.QWidget)
+ ui.bucketDataTab.SetObjectName("bucketDataTab")
+
+ ui.gridLayout_6 = qt.NewQGridLayout(ui.bucketDataTab)
+ ui.gridLayout_6.SetObjectName("gridLayout_6")
+ ui.gridLayout_6.SetContentsMargins(3, 3, 3, 3)
+ ui.gridLayout_6.SetSpacing(6)
+
+ ui.bucketData = qt.NewQTreeWidget2(ui.bucketDataTab)
+ ui.bucketData.SetObjectName("bucketData")
+ 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.AddWidget2(ui.bucketData.QWidget, 0, 0)
+
+ ui.AddDataButton = qt.NewQPushButton4(ui.bucketDataTab)
+ ui.AddDataButton.SetObjectName("AddDataButton")
+ 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.NewQPushButton4(ui.bucketDataTab)
+ ui.DeleteDataButton.SetObjectName("DeleteDataButton")
+ 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.NewQMenuBar2(ui.MainWindow.QWidget)
+ ui.menuBar.SetObjectName("menuBar")
+ ui.menuBar.Resize(668, 21)
+
+ ui.menuFile = qt.NewQMenu3(ui.menuBar.QWidget)
+ ui.menuFile.SetObjectName("menuFile")
+ 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.QWidget.AddAction(ui.actionExit)
+
+ ui.menuHelp = qt.NewQMenu3(ui.menuBar.QWidget)
+ ui.menuHelp.SetObjectName("menuHelp")
+ ui.menuHelp.QWidget.AddAction(ui.actionAbout_qbolt)
+ ui.menuHelp.QWidget.AddAction(ui.actionAbout_Qt)
+
+ ui.menuView = qt.NewQMenu3(ui.menuBar.QWidget)
+ ui.menuView.SetObjectName("menuView")
+ 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.NewQToolBar4(ui.MainWindow.QWidget)
+ ui.mainToolBar.SetObjectName("mainToolBar")
+ 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.NewQStatusBar2(ui.MainWindow.QWidget)
+ ui.statusBar.SetObjectName("statusBar")
+ 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_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.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.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("Fi&le"))
+ ui.menuHelp.SetTitle(qt.QMenuBar_Tr("Help"))
+ ui.menuView.SetTitle(qt.QMenuBar_Tr("&View"))
+}
diff --git a/qbolt/boltdb.cpp b/qbolt/boltdb.cpp
deleted file mode 100644
index 2aa8db3..0000000
--- a/qbolt/boltdb.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-#include "boltdb.h"
-
-#include
-
-BoltDB::BoltDB()
-{
-
-}
-
-BoltDB* BoltDB::createFrom(QString filePath, bool readOnly, QString &errorOut)
-{
- QByteArray filePathBytes(filePath.toUtf8());
- GoString filePathGS = Interop::toGoString_WeakRef(&filePathBytes);
-
- auto open_ret = ::Bolt_Open(readOnly, filePathGS);
- if (open_ret.r2 != 0) {
- errorOut = QString::fromUtf8(open_ret.r1, open_ret.r2);
- free(open_ret.r1);
- return nullptr;
- }
-
- BoltDB *ret = new BoltDB();
- ret->gmsDbRef = open_ret.r0;
- return ret;
-}
-
-static const int ERROR_AND_STOP_CALLING = 100;
-static const int ERROR_AND_KEEP_CALLING = 101;
-static const int FINISHED_OK = 102;
-static const int REAL_MESSAGE = 103;
-
-static bool handleTriple(int r0, char* r1, int64_t r2, QString& errorOut) {
- if (r0 == ERROR_AND_STOP_CALLING) {
- errorOut = QString::fromUtf8(r1, r2);
- free(r1);
- return false;
-
- } else if (r0 == FINISHED_OK) {
- return true;
-
- } else {
- // ?? unreachable
- return false;
-
- }
-}
-
-bool BoltDB::addBucket(const QList& bucketPath, QByteArray bucketName, QString& errorOut)
-{
- GoSliceManagedWrapper browse(bucketPath);
- GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
- auto resp = ::Bolt_CreateBucket(this->gmsDbRef, browse.slice, bucketNameGS);
-
- return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
-}
-
-bool BoltDB::deleteBucket(const QList& bucketPath, QByteArray bucketName, QString& errorOut)
-{
- GoSliceManagedWrapper browse(bucketPath);
- GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
- auto resp = ::Bolt_DeleteBucket(this->gmsDbRef, browse.slice, bucketNameGS);
-
- return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
-}
-
-bool BoltDB::setItem(const QList& bucketPath, QByteArray keyName, QByteArray value, QString& errorOut)
-{
- GoSliceManagedWrapper browse(bucketPath);
- GoString keyNameGS = Interop::toGoString_WeakRef(&keyName);
- GoString valueGS = Interop::toGoString_WeakRef(&value);
- auto resp = ::Bolt_SetItem(this->gmsDbRef, browse.slice, keyNameGS, valueGS);
-
- return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
-}
-
-bool BoltDB::deleteItem(const QList& bucketPath, QByteArray keyName, QString& errorOut)
-{
- GoSliceManagedWrapper browse(bucketPath);
- GoString keyNameGS = Interop::toGoString_WeakRef(&keyName);
- auto resp = ::Bolt_DeleteItem(this->gmsDbRef, browse.slice, keyNameGS);
-
- return handleTriple(resp.r0, resp.r1, resp.r2, errorOut);
-}
-
-
-bool BoltDB::listBucketsAtRoot(QString& errorOut, NameReciever cb)
-{
- auto listJob = ::Bolt_ListBucketsAtRoot(this->gmsDbRef);
- return pumpNext(listJob, errorOut, cb);
-}
-
-bool BoltDB::listBuckets(const QList& bucketPath, QString& errorOut, NameReciever cb)
-{
- if (bucketPath.size() == 0) {
- return listBucketsAtRoot(errorOut, cb);
- }
-
- GoSliceManagedWrapper browse(bucketPath);
- auto listJob = ::Bolt_ListBuckets(this->gmsDbRef, browse.slice);
- return pumpNext(listJob, errorOut, cb);
-}
-
-bool BoltDB::listKeys(const QList& bucketPath, QString& errorOut, std::function cb)
-{
- GoSliceManagedWrapper browse(bucketPath);
- auto listJob = ::Bolt_ListItems(this->gmsDbRef, browse.slice);
- return pumpNext(listJob, errorOut, [=](QByteArray b) {
- // First 8 bytes are little-endian uint64 len
- int64_t dataLen = qFromLittleEndian(b.mid(0, 8));
- cb(b.mid(8), dataLen);
- });
-}
-
-bool BoltDB::getData(const QList& bucketPath, QByteArray key, std::function onSuccess, std::function onError)
-{
- GoSliceManagedWrapper browse(bucketPath);
- GoString keyGS = Interop::toGoString_WeakRef(&key);
- auto resp = ::Bolt_GetItem(this->gmsDbRef, browse.slice, keyGS);
-
- if (resp.r0 == ERROR_AND_STOP_CALLING) {
- onError(QString::fromUtf8(resp.r1, resp.r2));
- free(resp.r1);
- return false;
-
- } else if (resp.r0 == REAL_MESSAGE) {
- onSuccess(QByteArray(resp.r1, resp.r2));
- free(resp.r1);
- return true;
-
- } else {
- // ?? unreachable
- return false;
-
- }
-}
-
-bool BoltDB::pumpNext(GoInt64 jobRef, QString& errorOut, NameReciever cb)
-{
- errorOut.clear();
-
- for(;;) {
- auto gnr = ::GetNext(jobRef);
-
- if (gnr.r0 == ERROR_AND_STOP_CALLING) {
- errorOut.append(QString::fromUtf8(gnr.r1, gnr.r2)); // log error
- free(gnr.r1);
- break; // done
-
- } else if (gnr.r0 == ERROR_AND_KEEP_CALLING) {
- errorOut.append(QString::fromUtf8(gnr.r1, gnr.r2)); // log error
- free(gnr.r1);
- continue;
-
- } else if (gnr.r0 == FINISHED_OK) {
- // Once we hit this, the go-side will clean up the channel / associated goroutines
- break;
-
- } else if (gnr.r0 == REAL_MESSAGE) {
- cb(QByteArray(gnr.r1, gnr.r2));
- free(gnr.r1);
- continue;
- }
- }
-
- return (errorOut.length() == 0);
-}
-
-bool BoltDB::getStatsJSON(std::function onSuccess, std::function onError)
-{
- auto statresp = Bolt_DBStats(this->gmsDbRef);
-
- if (statresp.r0 == ERROR_AND_STOP_CALLING) {
- onError(QString::fromUtf8(statresp.r1, statresp.r2));
- free(statresp.r1);
- return false;
-
- } else if (statresp.r0 == REAL_MESSAGE) {
- onSuccess(QByteArray(statresp.r1, statresp.r2));
- free(statresp.r1);
- return true;
-
- } else {
- // ?? shouldn't be reachable
- return false;
- }
-}
-
-bool BoltDB::getBucketStatsJSON(const QList& bucketPath, std::function onSuccess, std::function onError)
-{
- GoSliceManagedWrapper sliceWrapper(bucketPath);
- auto statresp = Bolt_BucketStats(this->gmsDbRef, sliceWrapper.slice);
-
- if (statresp.r0 == ERROR_AND_STOP_CALLING) {
- QString err = QString::fromUtf8(statresp.r1, statresp.r2);
- free(statresp.r1);
- onError(err);
- return false;
-
- } else if (statresp.r0 == REAL_MESSAGE) {
- onSuccess(QByteArray(statresp.r1, statresp.r2));
- free(statresp.r1);
- return true;
-
- } else {
- // ?? shouldn't be reachable
- return false;
- }
-}
-
-BoltDB::~BoltDB()
-{
- auto err = ::Bolt_Close(this->gmsDbRef);
- if (err.r1 != 0) {
- // Error closing database!
- // Need to display an alert... somewhere
-
- free(err.r0);
- }
-}
diff --git a/qbolt/boltdb.h b/qbolt/boltdb.h
deleted file mode 100644
index 65fe09e..0000000
--- a/qbolt/boltdb.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef BOLTDB_H
-#define BOLTDB_H
-
-#include "interop.h"
-#include
-
-typedef std::function NameReciever;
-
-class BoltDB
-{
-protected:
- BoltDB();
-
- GoInt64 gmsDbRef;
-
-public:
- static BoltDB* createFrom(QString filePath, bool readOnly, QString &errorOut);
-
- bool listBucketsAtRoot(QString& errorOut, NameReciever cb);
-
- bool listBuckets(const QList& bucketPath, QString& errorOut, NameReciever cb);
-
- bool addBucket(const QList& bucketPath, QByteArray bucketName, QString& errorOut);
-
- bool deleteBucket(const QList& bucketPath, QByteArray bucketName, QString& errorOut);
-
- bool setItem(const QList& bucketPath, QByteArray keyName, QByteArray value, QString& errorOut);
-
- bool deleteItem(const QList& bucketPath, QByteArray keyName, QString& errorOut);
-
- bool listKeys(const QList& bucketPath, QString& errorOut, std::function cb);
-
- bool getData(const QList& bucketPath, QByteArray key, std::function onSuccess, std::function onError);
-
- bool getStatsJSON(std::function onSuccess, std::function onError);
-
- bool getBucketStatsJSON(const QList& bucketPath, std::function onSuccess, std::function onError);
-
- ~BoltDB();
-
-protected:
-
- bool pumpNext(GoInt64 jobRef, QString& errorOut, NameReciever cb);
-};
-
-#endif // BOLTDB_H
diff --git a/qbolt/interop.cpp b/qbolt/interop.cpp
deleted file mode 100644
index a4e173f..0000000
--- a/qbolt/interop.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "interop.h"
-
-#include
-
-Interop::Interop()
-{
-
-}
-
-GoString Interop::toGoString_WeakRef(QByteArray *qba) {
- return GoString{qba->data(), qba->length()};
-}
-
-int64_t Interop::GetMagic() {
- return ::GetMagic();
-}
-
-//
-
-GoSliceManagedWrapper::GoSliceManagedWrapper(const QList& qsl) :
- rawStrings(),
- slice(),
- strings(nullptr)
-{
- rawStrings.reserve(qsl.size());
- strings = new GoString[qsl.size()];
-
- for (int i = 0; i < qsl.size(); ++i) {
- rawStrings.push_back( qsl.at(i) );
- strings[i].p = rawStrings[i].data();
- strings[i].n = rawStrings[i].size();
- }
-
- slice.data = static_cast(strings);
- slice.len = qsl.size(); // * sizeof(GoString);
- slice.cap = slice.len;
-}
-
-GoSliceManagedWrapper::~GoSliceManagedWrapper()
-{
- delete[] strings;
-}
diff --git a/qbolt/interop.h b/qbolt/interop.h
deleted file mode 100644
index fabb163..0000000
--- a/qbolt/interop.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef INTEROP_H
-#define INTEROP_H
-
-#include "qbolt_cgo.h"
-#include
-#include
-
-class GoSliceManagedWrapper {
- Q_DISABLE_COPY(GoSliceManagedWrapper)
-
-public:
- GoSliceManagedWrapper(const QList& qsl);
- ~GoSliceManagedWrapper();
-protected:
- QList rawStrings;
-public:
- GoSlice slice;
- GoString *strings;
-};
-
-class Interop
-{
-public:
- Interop();
-
- static GoString toGoString_WeakRef(QByteArray *qba);
-
- static int64_t GetMagic();
-};
-
-#endif // INTEROP_H
diff --git a/qbolt/itemwindow.cpp b/qbolt/itemwindow.cpp
deleted file mode 100644
index 6ba97bf..0000000
--- a/qbolt/itemwindow.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "itemwindow.h"
-#include "ui_itemwindow.h"
-
-ItemWindow::ItemWindow(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::ItemWindow)
-{
- ui->setupUi(this);
-}
-
-ItemWindow::~ItemWindow()
-{
- delete ui;
-}
-
-QPlainTextEdit* ItemWindow::ContentArea() const {
- return ui->contentArea;
-}
diff --git a/qbolt/itemwindow.h b/qbolt/itemwindow.h
deleted file mode 100644
index 326ae52..0000000
--- a/qbolt/itemwindow.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef ITEMWINDOW_H
-#define ITEMWINDOW_H
-
-#include
-#include
-
-namespace Ui {
-class ItemWindow;
-}
-
-class ItemWindow : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit ItemWindow(QWidget *parent = 0);
- ~ItemWindow();
-
- QPlainTextEdit* ContentArea() const;
-
-private:
- Ui::ItemWindow *ui;
-};
-
-#endif // ITEMWINDOW_H
diff --git a/qbolt/main.cpp b/qbolt/main.cpp
deleted file mode 100644
index 7ee0f34..0000000
--- a/qbolt/main.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "mainwindow.h"
-#include
-#include "interop.h"
-
-#include
-
-int main(int argc, char *argv[])
-{
- int64_t magic = Interop::GetMagic();
- if (magic != 0x10203040) {
- qDebug() << "bad magic " << magic;
- return 1;
- }
-
- QApplication a(argc, argv);
- QApplication::setApplicationDisplayName("QBolt");
- QApplication::setWindowIcon(QIcon(":/rsrc/database_lightning.png"));
-
- MainWindow w;
- w.show();
-
- return a.exec();
-}
diff --git a/qbolt/mainwindow.cpp b/qbolt/mainwindow.cpp
deleted file mode 100644
index d149055..0000000
--- a/qbolt/mainwindow.cpp
+++ /dev/null
@@ -1,478 +0,0 @@
-#include "mainwindow.h"
-#include "ui_mainwindow.h"
-#include "itemwindow.h"
-#include "boltdb.h"
-
-#include
-#include
-#include
-#include
-
-MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
-{
- ui->setupUi(this);
-
- on_bucketTree_currentItemChanged(nullptr, nullptr);
-
- databaseContext = new QMenu();
- databaseContext->addAction(ui->actionRefresh_buckets);
- databaseContext->addAction(ui->actionAdd_bucket);
- databaseContext->addSeparator();
- databaseContext->addAction(ui->actionDisconnect);
-
- bucketContext = new QMenu();
- bucketContext->addAction(ui->actionRefresh_buckets);
- bucketContext->addAction(ui->actionAdd_bucket);
- bucketContext->addSeparator();
- bucketContext->addAction(ui->actionDelete_bucket);
-}
-
-MainWindow::~MainWindow()
-{
- delete ui;
-}
-
-static const int BdbPointerRole = Qt::UserRole + 1;
-static const int BinaryDataRole = Qt::UserRole + 2;
-
-#define SET_BDB(top, bdb) top->setData(0, BdbPointerRole, QVariant::fromValue(static_cast(bdb)))
-#define GET_BDB(top) static_cast( top->data(0, BdbPointerRole).value() )
-
-void MainWindow::on_actionNew_database_triggered()
-{
- QString file = QFileDialog::getSaveFileName(this, tr("Save new bolt database as..."));
- if (file.length()) {
- openDatabase(file, false);
- }
-}
-
-void MainWindow::on_actionOpen_database_triggered()
-{
- QString file = QFileDialog::getOpenFileName(this, tr("Select bolt database..."));
- if (file.length()) {
- openDatabase(file, false);
- }
-}
-
-void MainWindow::on_actionOpen_database_as_read_only_triggered()
-{
- QString file = QFileDialog::getOpenFileName(this, tr("Select bolt database..."));
- if (file.length()) {
- openDatabase(file, true);
- }
-}
-
-void MainWindow::openDatabase(QString file, bool readOnly)
-{
- // Open
- QString error;
- auto *bdb = BoltDB::createFrom(file, readOnly, error);
- if (bdb == nullptr) {
- QMessageBox qmb;
- qmb.setText(tr("Error opening database: %1").arg(error));
- qmb.exec();
- return;
- }
-
- QTreeWidgetItem *top = new QTreeWidgetItem();
- top->setText(0, QFileInfo(file).fileName());
- top->setIcon(0, QIcon(":/rsrc/database.png"));
- SET_BDB(top, bdb);
- ui->bucketTree->addTopLevelItem(top);
-
- refreshBucketTree(top);
- ui->bucketTree->setCurrentItem(top);
-
- ui->bucketTree->expandItem(top);
-}
-
-static const QString getDisplayName(const QByteArray &qba) {
- // FIXME the formatting isn't so great when control characters, etc. are used
- // A C-style escape display, or the unicode-replacement-character would be preferable
- QString ret(QString::fromUtf8(qba));
-
- bool allPrintable = true;
- for (auto i = ret.begin(), e = ret.end(); i != e; ++i) {
- if (! i->isPrint()) {
- allPrintable = false;
- break;
- }
- }
-
- if (allPrintable) {
- return ret; // fine
- }
-
- // Some of the characters weren't printable.
- // Build up a replacement string
- QString replacement;
- for (auto i = ret.begin(), e = ret.end(); i != e; ++i) {
- replacement += i->isPrint() ? *i : QStringLiteral("\\u{%1}").arg(i->unicode());
- }
- return replacement;
-}
-
-void MainWindow::refreshBucketTree(QTreeWidgetItem* itm)
-{
- QTreeWidgetItem *top = itm;
- QList browsePath;
- while(top->parent() != nullptr) {
- browsePath.push_front(top->data(0, BinaryDataRole).toByteArray());
- top = top->parent();
- }
-
- // Remove existing children
- for (int i = itm->childCount(); i --> 0;) {
- delete itm->takeChild(i);
- }
-
- auto *bdb = GET_BDB(top);
-
- QString error;
- bool ok = bdb->listBuckets(
- browsePath,
- error,
- [=](QByteArray qba){
- QTreeWidgetItem *child = new QTreeWidgetItem();
- child->setText(0, getDisplayName(qba));
- child->setData(0, BinaryDataRole, qba);
- child->setIcon(0, QIcon(":/rsrc/table.png"));
- itm->addChild(child);
-
- refreshBucketTree(child);
- }
- );
-
- if (!ok) {
- QMessageBox qmb;
- qmb.setText(tr("Error listing buckets: %1").arg(error));
- qmb.exec();
- // (continue)
- }
-}
-
-void MainWindow::on_actionExit_triggered()
-{
- close();
-}
-
-void MainWindow::on_actionAbout_Qt_triggered()
-{
- QApplication::aboutQt();
-}
-
-void MainWindow::on_actionAbout_qbolt_triggered()
-{
- QMessageBox::about(
- this,
- QApplication::applicationDisplayName(),
- "QBolt
Graphical interface for managing Bolt databases
"
- "- About BoltDB
"
- "- FamFamFam "Silk" icon set
"
- "- QBolt homepage
"
- );
-}
-
-void MainWindow::on_actionDisconnect_triggered()
-{
- QTreeWidgetItem *top = lastContextSelection;
- if (top->parent()) {
- return; // somehow we didn't select a top-level item
- }
-
- auto *bdb = GET_BDB(top);
-
- // Remove UI
- ui->bucketTree->clearSelection();
- delete top;
-
- // Disconnect from DB
- delete bdb;
-}
-
-void MainWindow::on_bucketTree_customContextMenuRequested(const QPoint &pos)
-{
- auto *itm = ui->bucketTree->itemAt(pos);
- if (itm == nullptr) {
- return;
- }
-
- lastContextSelection = itm;
-
- if (itm->parent() != nullptr) {
- // Child item, show the bucket menu
- bucketContext->popup(ui->bucketTree->mapToGlobal(pos));
-
- } else {
- // Top-level item, show the database menu
- databaseContext->popup(ui->bucketTree->mapToGlobal(pos));
- }
-}
-
-void MainWindow::on_actionRefresh_buckets_triggered()
-{
- refreshBucketTree(lastContextSelection);
-}
-
-void MainWindow::on_bucketTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
-{
- Q_UNUSED(previous);
- if (current == nullptr) {
- ui->stackedWidget->setVisible(false);
- return;
- }
-
- ui->stackedWidget->setVisible(true);
-
- if (current->parent() == nullptr) {
- // Selected a database
- ui->stackedWidget->setCurrentWidget(ui->databasePage);
- ui->databasePropertiesArea->clear();
-
- auto *bdb = GET_BDB(current);
- bdb->getStatsJSON(
- [=](QByteArray j) {
- auto doc = QJsonDocument::fromJson(j);
- ui->databasePropertiesArea->setPlainText(QString::fromUtf8(doc.toJson(QJsonDocument::Indented)));
- },
- [=](QString error) {
- ui->databasePropertiesArea->setPlainText(tr("Error retrieving database statistics: %1").arg(error));
- }
- );
-
- // Clean up foreign areas
- ui->bucketPropertiesArea->clear();
- ui->bucketData->clear();
-
- } else {
- // Selected a bucket
-
- ui->stackedWidget->setCurrentWidget(ui->bucketPage);
- ui->bucketPropertiesArea->clear();
-
- QList browse;
- QTreeWidgetItem *top = current;
- while (top->parent() != nullptr) {
- browse.push_front(top->data(0, BinaryDataRole).toByteArray());
- top = top->parent();
- }
- auto *bdb = GET_BDB(top);
-
- bdb->getBucketStatsJSON(
- browse,
- [=](QByteArray j) {
- auto doc = QJsonDocument::fromJson(j);
- ui->bucketPropertiesArea->setPlainText(QString::fromUtf8(doc.toJson(QJsonDocument::Indented)));
- },
- [=](QString error) {
- ui->bucketPropertiesArea->setPlainText(tr("Error retrieving bucket statistics: %1").arg(error));
- }
- );
-
- // Load the data tab
- refreshData(bdb, browse);
-
- // Clean up foreign areas
- ui->databasePropertiesArea->clear();
- }
-}
-
-void MainWindow::refreshData(BoltDB *bdb, const QList& browse)
-{
- // Load the data tab
- ui->bucketData->clear();
- QString err;
- bool ok = bdb->listKeys(browse, err, [=](QByteArray name, int64_t dataLen) {
- auto *itm = new QTreeWidgetItem();
- itm->setText(0, getDisplayName(name));
- itm->setData(0, BinaryDataRole, name);
- itm->setText(1, QString("%1").arg(dataLen));
- ui->bucketData->addTopLevelItem(itm);
- });
-
- if (! ok) {
- QMessageBox qmb;
- qmb.setText(tr("Error listing bucket content: %1").arg(err));
- qmb.exec();
- }
-
- ui->bucketData->resizeColumnToContents(0);
- on_bucketData_itemSelectionChanged();
-}
-
-void MainWindow::on_actionClear_selection_triggered()
-{
- ui->bucketTree->setCurrentItem(nullptr);
-}
-
-#define GET_ITM_TOP_BROWSE_BDB \
- QTreeWidgetItem* itm = ui->bucketTree->currentItem(); \
- if (itm == nullptr) { \
- return; \
- } \
- QTreeWidgetItem* top = itm; \
- QList browse; \
- while(top->parent() != nullptr) { \
- browse.push_front(top->data(0, BinaryDataRole).toByteArray()); \
- top = top->parent(); \
- } \
- auto *bdb = GET_BDB(top);
-
-void MainWindow::openEditor(BoltDB *bdb, const QList& saveAs, QByteArray saveAsKey, QByteArray currentContent)
-{
- auto iw = new ItemWindow();
- iw->ContentArea()->setPlainText(QString::fromUtf8(currentContent));
- iw->setWindowTitle(QString::fromUtf8(saveAsKey));
- iw->setWindowModality(Qt::ApplicationModal); // we need this - otherwise we'll refresh a possibly-changed area after saving
- connect(iw, &ItemWindow::finished, iw, [=](int exitCode){
- if (exitCode == ItemWindow::Accepted) {
- QString err;
- if (! bdb->setItem(saveAs, saveAsKey, iw->ContentArea()->toPlainText().toUtf8(), err)) {
- QMessageBox qmb;
- qmb.setText(tr("Error saving item content: %1").arg(err));
- qmb.exec();
- }
-
- refreshData(bdb, saveAs);
- }
- iw->deleteLater();
- });
-
- iw->show();
-}
-
-void MainWindow::on_bucketData_doubleClicked(const QModelIndex &index)
-{
- GET_ITM_TOP_BROWSE_BDB;
-
- // Get item key
-
- auto model = index.model();
- const QByteArray& key = model->data(model->index(index.row(), 0), BinaryDataRole).toByteArray();
-
- // DB lookup
-
- bdb->getData(
- browse,
- key,
- [=](QByteArray content) {
- openEditor(bdb, browse, key, content);
- },
- [=](QString error) {
- QMessageBox qmb;
- qmb.setText(tr("Error loading item content: %1").arg(error));
- qmb.exec();
- }
- );
-
-}
-
-void MainWindow::on_actionAdd_bucket_triggered()
-{
- GET_ITM_TOP_BROWSE_BDB;
-
- // Prompt for bucket name
-
- QString name = QInputDialog::getText(this, tr("New bucket"), tr("Enter a key for the new bucket:"));
- if (name.length() == 0) {
- return;
- }
-
- // Create
- QString err;
- if (! bdb->addBucket(browse, name.toUtf8(), err)) {
- QMessageBox qmb;
- qmb.setText(tr("Error creating bucket: %1").arg(err));
- qmb.exec();
- return;
- }
-
- // Refresh bucket list
- refreshBucketTree(itm); // sub-tree only
- ui->bucketTree->expandItem(itm);
-}
-
-void MainWindow::on_actionDelete_bucket_triggered()
-{
- GET_ITM_TOP_BROWSE_BDB;
-
- // Prompt for confirmation
- const QByteArray& bucketToDelete = itm->data(0, BinaryDataRole).toByteArray();
- if (
- QMessageBox::question(
- this,
- tr("Delete bucket"),
- tr("Are you sure you want to remove the bucket '%1'?").arg(getDisplayName(bucketToDelete)),
- QMessageBox::Yes,
- QMessageBox::Cancel
- ) != QMessageBox::Yes
- ) {
- return;
- }
-
- QTreeWidgetItem* parent = itm->parent();
-
- // One level down
-
- browse.pop_back();
-
- QString err;
- if (! bdb->deleteBucket(browse, bucketToDelete, err)) {
- QMessageBox qmb;
- qmb.setText(tr("Error removing bucket: %1").arg(err));
- qmb.exec();
- return;
- }
-
- // Refresh bucket list
- refreshBucketTree(parent); // sub-tree only
- ui->bucketTree->expandItem(parent);
- ui->bucketTree->setCurrentItem(parent);
-}
-
-void MainWindow::on_AddDataButton_clicked()
-{
- GET_ITM_TOP_BROWSE_BDB;
-
- // Prompt for bucket name
-
- QString name = QInputDialog::getText(this, tr("New item"), tr("Enter a key for the new item:"));
- if (name.length() == 0) {
- return;
- }
-
- openEditor(bdb, browse, name.toUtf8(), QByteArray());
-}
-
-void MainWindow::on_DeleteDataButton_clicked()
-{
- GET_ITM_TOP_BROWSE_BDB;
-
- auto selection = ui->bucketData->selectedItems();
- if (selection.length() == 0) {
- return; // nothing to do
- }
-
- // Prompt for confirmation
- if (QMessageBox::question(this, tr("Delete items"), tr("Are you sure you want to remove %1 item(s)?").arg(selection.length()), QMessageBox::Yes, QMessageBox::Cancel) != QMessageBox::Yes) {
- return;
- }
-
- QString err;
- for (int i = selection.length(); i-->0;) {
- if (! bdb->deleteItem(browse, selection[i]->data(0, BinaryDataRole).toByteArray(), err)) {
- QMessageBox qmb;
- qmb.setText(tr("Error removing item: %1").arg(err));
- qmb.exec();
- }
- }
-
- refreshData(bdb, browse);
-}
-
-void MainWindow::on_bucketData_itemSelectionChanged()
-{
- ui->DeleteDataButton->setEnabled( (ui->bucketData->selectedItems().size() > 0) );
-}
diff --git a/qbolt/mainwindow.h b/qbolt/mainwindow.h
deleted file mode 100644
index ae541c8..0000000
--- a/qbolt/mainwindow.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef MAINWINDOW_H
-#define MAINWINDOW_H
-
-#include "boltdb.h"
-#include
-#include
-#include
-
-namespace Ui {
-class MainWindow;
-}
-
-class MainWindow : public QMainWindow
-{
- Q_OBJECT
-
-public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
-
-private slots:
- void on_actionOpen_database_triggered();
-
- void on_actionExit_triggered();
-
- void on_actionAbout_Qt_triggered();
-
- void on_actionAbout_qbolt_triggered();
-
- void on_actionDisconnect_triggered();
-
- void on_bucketTree_customContextMenuRequested(const QPoint &pos);
-
- void on_actionRefresh_buckets_triggered();
-
- void on_bucketTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
-
- void on_actionClear_selection_triggered();
-
- void on_bucketData_doubleClicked(const QModelIndex &index);
-
- void on_actionNew_database_triggered();
-
- void on_actionAdd_bucket_triggered();
-
- void on_actionDelete_bucket_triggered();
-
- void on_AddDataButton_clicked();
-
- void on_DeleteDataButton_clicked();
-
- void on_bucketData_itemSelectionChanged();
-
- void on_actionOpen_database_as_read_only_triggered();
-
-protected:
- void openDatabase(QString file, bool readOnly);
- void refreshBucketTree(QTreeWidgetItem* top);
- void refreshData(BoltDB *bdb, const QList& browse);
- void openEditor(BoltDB *bdb, const QList& saveAs, QByteArray saveAsKey, QByteArray currentContent);
-
-private:
- Ui::MainWindow *ui;
-
- QMenu *databaseContext;
- QMenu *bucketContext;
- QTreeWidgetItem* lastContextSelection;
-};
-
-#endif // MAINWINDOW_H
diff --git a/qbolt/qbolt.pro b/qbolt/qbolt.pro
deleted file mode 100644
index 402b58d..0000000
--- a/qbolt/qbolt.pro
+++ /dev/null
@@ -1,48 +0,0 @@
-#-------------------------------------------------
-#
-# Project created by QtCreator 2017-05-15T19:38:33
-#
-#-------------------------------------------------
-
-QT += core gui
-
-greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
-
-TARGET = qbolt
-TEMPLATE = app
-
-# Enforce Qt deprecations
-DEFINES += QT_DEPRECATED_WARNINGS
-DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000
-
-win32: {
- # for some reason, qbolt_cgo.h never realises that Q_OS_WIN is defined for win32 builds... weird
- DEFINES += CGO_WINDOWS
- QMAKE_LIBS += $$_PRO_FILE_PWD_/../build/win32/qbolt.a
- QMAKE_LIBS += -lntdll
-
- RC_ICONS = rsrc/qbolt.ico
-}
-
-linux: {
- QMAKE_LIBS += $$_PRO_FILE_PWD_/../build/linux/qbolt.a
- QMAKE_LIBS += -lpthread
-}
-
-SOURCES += main.cpp\
- mainwindow.cpp \
- interop.cpp \
- boltdb.cpp \
- itemwindow.cpp
-
-HEADERS += mainwindow.h \
- interop.h \
- boltdb.h \
- qbolt_cgo.h \
- itemwindow.h
-
-FORMS += mainwindow.ui \
- itemwindow.ui
-
-RESOURCES += \
- resources.qrc
diff --git a/qbolt/qbolt_cgo.h b/qbolt/qbolt_cgo.h
deleted file mode 100644
index 1c000d1..0000000
--- a/qbolt/qbolt_cgo.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef QBOLT_CGO_H
-#define QBOLT_CGO_H
-
-#if defined(Q_OS_WIN) || defined(CGO_WINDOWS)
-#include "../build/win32/qbolt.h"
-#else
-#include "../build/linux/qbolt.h"
-#endif
-
-#endif // QBOLT_CGO_H
diff --git a/resources.go b/resources.go
new file mode 100644
index 0000000..20abcf2
--- /dev/null
+++ b/resources.go
@@ -0,0 +1,17 @@
+package main
+
+//go:generate miqt-rcc.sh resources.qrc
+
+import (
+ "embed"
+
+ "github.com/mappu/miqt/qt"
+)
+
+//go:embed resources.rcc
+var _resourceRcc []byte
+
+func init() {
+ _ = embed.FS{}
+ qt.QResource_RegisterResourceWithRccData(&_resourceRcc[0])
+}
diff --git a/qbolt/resources.qrc b/resources.qrc
similarity index 100%
rename from qbolt/resources.qrc
rename to resources.qrc
diff --git a/resources.rcc b/resources.rcc
new file mode 100644
index 0000000..0f693c5
Binary files /dev/null and b/resources.rcc differ
diff --git a/qbolt/rsrc/add.png b/rsrc/add.png
similarity index 100%
rename from qbolt/rsrc/add.png
rename to rsrc/add.png
diff --git a/qbolt/rsrc/arrow_refresh.png b/rsrc/arrow_refresh.png
similarity index 100%
rename from qbolt/rsrc/arrow_refresh.png
rename to rsrc/arrow_refresh.png
diff --git a/qbolt/rsrc/chart_bar.png b/rsrc/chart_bar.png
similarity index 100%
rename from qbolt/rsrc/chart_bar.png
rename to rsrc/chart_bar.png
diff --git a/qbolt/rsrc/database.png b/rsrc/database.png
similarity index 100%
rename from qbolt/rsrc/database.png
rename to rsrc/database.png
diff --git a/qbolt/rsrc/database_add.png b/rsrc/database_add.png
similarity index 100%
rename from qbolt/rsrc/database_add.png
rename to rsrc/database_add.png
diff --git a/qbolt/rsrc/database_connect.png b/rsrc/database_connect.png
similarity index 100%
rename from qbolt/rsrc/database_connect.png
rename to rsrc/database_connect.png
diff --git a/qbolt/rsrc/database_lightning.png b/rsrc/database_lightning.png
similarity index 100%
rename from qbolt/rsrc/database_lightning.png
rename to rsrc/database_lightning.png
diff --git a/qbolt/rsrc/delete.png b/rsrc/delete.png
similarity index 100%
rename from qbolt/rsrc/delete.png
rename to rsrc/delete.png
diff --git a/qbolt/rsrc/disconnect.png b/rsrc/disconnect.png
similarity index 100%
rename from qbolt/rsrc/disconnect.png
rename to rsrc/disconnect.png
diff --git a/qbolt/rsrc/door_out.png b/rsrc/door_out.png
similarity index 100%
rename from qbolt/rsrc/door_out.png
rename to rsrc/door_out.png
diff --git a/qbolt/rsrc/information.png b/rsrc/information.png
similarity index 100%
rename from qbolt/rsrc/information.png
rename to rsrc/information.png
diff --git a/qbolt/rsrc/page.png b/rsrc/page.png
similarity index 100%
rename from qbolt/rsrc/page.png
rename to rsrc/page.png
diff --git a/qbolt/rsrc/qbolt.ico b/rsrc/qbolt.ico
similarity index 100%
rename from qbolt/rsrc/qbolt.ico
rename to rsrc/qbolt.ico
diff --git a/qbolt/rsrc/table.png b/rsrc/table.png
similarity index 100%
rename from qbolt/rsrc/table.png
rename to rsrc/table.png
diff --git a/qbolt/rsrc/table_add.png b/rsrc/table_add.png
similarity index 100%
rename from qbolt/rsrc/table_add.png
rename to rsrc/table_add.png
diff --git a/qbolt/rsrc/table_delete.png b/rsrc/table_delete.png
similarity index 100%
rename from qbolt/rsrc/table_delete.png
rename to rsrc/table_delete.png
diff --git a/util.go b/util.go
new file mode 100644
index 0000000..5545995
--- /dev/null
+++ b/util.go
@@ -0,0 +1,9 @@
+package main
+
+// 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]
+ }
+}