func createTxInfo() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal(err) } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) l, err := gtk.LabelNew("") if err != nil { log.Fatal(err) } l.SetMarkup("<b>Recent Transactions</b>") l.OverrideFont("sans-serif 10") l.SetHAlign(gtk.ALIGN_START) grid.Add(l) txGrid, err := gtk.GridNew() if err != nil { log.Fatal(err) } txGrid.SetOrientation(gtk.ORIENTATION_VERTICAL) grid.Add(txGrid) Overview.Txs = txGrid return &grid.Container.Widget }
func createStatusbar() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal(err) } l, err := gtk.LabelNew("Connecting to daemon...") if err != nil { log.Fatal("Unable to create label:", err) } StatusElems.Lab = l grid.Add(l) p, err := gtk.ProgressBarNew() if err != nil { log.Fatal("Unable to create progress bar:", err) } StatusElems.Pb = p p.SetSizeRequest(300, -1) p.SetFraction(0) p.Set("show-text", true) p.SetText("") p.SetNoShowAll(true) grid.Add(p) return &grid.Container.Widget }
func windowWidget() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal("Unable to create grid:", err) } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) topLabel, err = gtk.LabelNew("Text set by initializer") if err != nil { log.Fatal("Unable to create label:", err) } bottomLabel, err = gtk.LabelNew("Text set by initializer") if err != nil { log.Fatal("Unable to create label:", err) } grid.Add(topLabel) grid.Add(bottomLabel) topLabel.SetHExpand(true) topLabel.SetVExpand(true) bottomLabel.SetHExpand(true) bottomLabel.SetVExpand(true) return &grid.Container.Widget }
func createOverview() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal(err) } grid.SetColumnHomogeneous(true) grid.Add(createWalletInfo()) grid.Add(createTxInfo()) return &grid.Container.Widget }
// openNewPage adds the page content and title label to the notebook. A close // tab button is added to the title label to create the notebook tab widget. // When the close button is pressed, the page will be removed from the manager. func (p *PageManager) openNewPage(page Page) int { // Create tab content using title label and connect necessary signals. tabContent, _ := gtk.GridNew() image, _ := gtk.ImageNewFromIconName("window-close", notebookIconSize) closeButton, _ := gtk.ButtonNew() closeButton.SetImage(image) closeButton.SetCanFocus(false) tabContent.Add(closeButton) title := page.TitleLabel() title.SetCanFocus(false) title.SetSizeRequest(150, -1) tabContent.Add(title) tabContent.SetCanFocus(false) closeButton.Connect("clicked", func() { pageNum := p.PageNum(page) p.RemovePage(pageNum) switch page := page.(type) { case *HTMLPage: delete(p.htmls, page.Native()) for _, s := range page.signals { s.Object.HandlerDisconnect(s.SignalHandle) } // Do not wait for the garbage collector to finalize // the webview and reap the web process. page.wv.Destroy() case *DownloadsPage: p.downloads = nil case *SettingsPage: p.settings = nil } // Always show at least one page. This defaults to a blank // HTML page. if p.GetNPages() == 0 { p.OpenPage(BlankPage) } }) tabContent.ShowAll() page.Show() n := p.AppendPage(page, tabContent) p.GrabFocus() p.SetTabReorderable(page, true) return n }
// newHTMLPage creates a new HTML page and begins loading the URI in the // description. func (d HTMLPageDescription) newHTMLPage() *HTMLPage { grid, _ := gtk.GridNew() grid.SetOrientation(gtk.ORIENTATION_VERTICAL) navbar := NewNavigationBar() navbar.SetHExpand(true) wv := wk2.NewWebView() wv.SetHExpand(true) wv.SetVExpand(true) title, _ := gtk.LabelNew("New Tab") title.SetEllipsize(pango.ELLIPSIZE_END) crash, _ := gtk.LabelNew("WebKit crashed :'(") grid.SetCanFocus(false) navbar.SetCanFocus(false) title.SetCanFocus(false) grid.Add(navbar) navbar.Show() grid.Add(wv) grid.Show() stack, _ := gtk.StackNew() stack.SetCanFocus(false) stack.AddNamed(grid, "webview") stack.AddNamed(crash, "crash") stack.SetVisibleChild(crash) page := &HTMLPage{ Stack: stack, title: "New Tab", uri: string(d), titleLabel: title, navbar: navbar, wv: wv, crash: crash, } page.signals = append(page.signals, page.connectNavbarSignals()...) page.signals = append(page.signals, page.connectWebKitSignals()...) page.setURI(string(d)) stack.SetVisibleChild(grid) wv.Show() page.LoadURI(string(d)) return page }
func createWalletInfo() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal(err) } header, err := gtk.LabelNew("") if err != nil { log.Fatal(err) } header.SetMarkup("<b>Wallet</b>") header.OverrideFont("sans-serif 16") header.SetHAlign(gtk.ALIGN_START) grid.Attach(header, 0, 0, 1, 1) balance, err := gtk.LabelNew("Balance:") if err != nil { log.Fatal(err) } balance.SetHAlign(gtk.ALIGN_START) grid.Attach(balance, 0, 1, 1, 1) unconfirmed, err := gtk.LabelNew("Unconfirmed:") if err != nil { log.Fatal(err) } unconfirmed.SetHAlign(gtk.ALIGN_START) grid.Attach(unconfirmed, 0, 2, 1, 1) balance, err = gtk.LabelNew("") if err != nil { log.Fatal(err) } balance.SetHAlign(gtk.ALIGN_START) grid.Attach(balance, 1, 1, 1, 1) Overview.Balance = balance unconfirmed, err = gtk.LabelNew("") if err != nil { log.Fatal(err) } grid.Attach(unconfirmed, 1, 2, 1, 1) Overview.Unconfirmed = unconfirmed /* transactions, err := gtk.LabelNew("Number of transactions:") if err != nil { log.Fatal(err) } transactions.SetHAlign(gtk.ALIGN_START) grid.Attach(transactions, 0, 3, 1, 1) transactions, err = gtk.LabelNew("a lot") if err != nil { log.Fatal(err) } transactions.SetHAlign(gtk.ALIGN_START) grid.Attach(transactions, 1, 3, 1, 1) Overview.NTransactions = transactions */ return &grid.Container.Widget }
func createNewWalletDialog() (*gtk.Dialog, error) { dialog, err := gtk.DialogNew() if err != nil { return nil, err } dialog.SetTitle("New wallet") dialog.AddButton("_OK", gtk.RESPONSE_OK) dialog.SetDefaultGeometry(500, 100) grid, err := gtk.GridNew() if err != nil { return nil, err } grid.SetHExpand(true) grid.SetVExpand(true) b, err := dialog.GetContentArea() if err != nil { return nil, err } b.Add(grid) // Because the label will wrap and the final minimum heights // and widths will be absurdly large, first give a size request and // show the grid (allocating space for the requested size). This will // make text wrapping labels size nicely inside the grid. grid.SetSizeRequest(500, 100) grid.Show() l, err := gtk.LabelNew("") if err != nil { return nil, err } l.SetLineWrap(true) l.SetMarkup(newWalletMessage) l.SetAlignment(0, 0) grid.Attach(l, 0, 0, 2, 1) b.SetHExpand(true) b.SetVExpand(true) l, err = gtk.LabelNew("Enter passphrase:") if err != nil { return nil, err } l.SetAlignment(1.0, 0.5) grid.Attach(l, 0, 1, 1, 1) passphrase, err := gtk.EntryNew() if err != nil { return nil, err } passphrase.SetVisibility(false) passphrase.SetHExpand(true) passphrase.Connect("activate", func() { dialog.Emit("response", gtk.RESPONSE_OK, nil) }) grid.Attach(passphrase, 1, 1, 1, 1) l, err = gtk.LabelNew("Confirm passphrase:") if err != nil { return nil, err } l.SetAlignment(1.0, 0.5) grid.Attach(l, 0, 2, 1, 1) repeated, err := gtk.EntryNew() if err != nil { return nil, err } repeated.SetVisibility(false) repeated.SetVAlign(gtk.ALIGN_START) repeated.Connect("activate", func() { dialog.Emit("response", gtk.RESPONSE_OK, nil) }) grid.Attach(repeated, 1, 2, 1, 1) showEntryText, err := gtk.CheckButtonNewWithLabel("Show passphrase") if err != nil { return nil, err } showEntryText.Connect("toggled", func() { active := showEntryText.GetActive() passphrase.SetVisibility(active) repeated.SetVisibility(active) }) grid.Attach(showEntryText, 1, 3, 2, 1) dialog.SetTransientFor(mainWindow) dialog.SetPosition(gtk.WIN_POS_CENTER_ON_PARENT) dialog.ShowAll() dialog.Connect("response", func(_ *glib.Object, rt gtk.ResponseType) { switch rt { case gtk.RESPONSE_OK: pStr, err := passphrase.GetText() if err != nil { log.Print(err) return } rStr, err := repeated.GetText() if err != nil { log.Print(err) return } if len(pStr) == 0 { mDialog := gtk.MessageDialogNew(dialog, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "A passphrase must be entered to create a wallet.") mDialog.SetTitle("Wallet creation failed") mDialog.Run() mDialog.Destroy() return } if pStr == rStr { go func() { triggers.newWallet <- &NewWalletParams{ passphrase: pStr, } if err := <-triggerReplies.walletCreationErr; err != nil { glib.IdleAdd(func() { mDialog := gtk.MessageDialogNew(dialog, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, err.Error()) mDialog.SetTitle("Wallet creation failed") mDialog.Run() mDialog.Destroy() }) } else { glib.IdleAdd(func() { dialog.Destroy() }) } }() } else { msg := "The supplied passphrases do not match." mDialog := gtk.MessageDialogNew(dialog, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg) mDialog.SetTitle("Wallet creation failed") mDialog.Run() mDialog.Destroy() } case gtk.RESPONSE_CANCEL: dialog.Destroy() } }) dialog.Connect("delete-event", func() { mDialog := gtk.MessageDialogNew(mainWindow, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "btcgui cannot be used without a wallet and will now close.") mDialog.Show() mDialog.Run() mDialog.Destroy() gtk.MainQuit() }) return dialog, nil }
func createAddrBook() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal(err) } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) store, err := gtk.ListStoreNew(glib.TYPE_STRING, glib.TYPE_STRING) if err != nil { log.Fatal(err) } tv, err := gtk.TreeViewNew() if err != nil { log.Fatal(err) } tv.SetModel(store) addrBookWidgets.store = store addrBookWidgets.treeview = tv sw, err := gtk.ScrolledWindowNew(nil, nil) if err != nil { log.Fatal(err) } sw.Add(tv) sw.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.SetHExpand(true) sw.SetVExpand(true) grid.Add(sw) renderer, err := gtk.CellRendererTextNew() if err != nil { log.Fatal(err) } renderer.Set("editable", true) renderer.Set("editable-set", true) renderer.Connect("edited", func(_ *glib.Object, path, text string) { iter, err := store.GetIterFromString(path) if err == nil { store.Set(iter, []int{0}, []interface{}{text}) } }) col, err := gtk.TreeViewColumnNewWithAttribute("Label", renderer, "text", 0) if err != nil { log.Fatal(err) } col.SetExpand(true) tv.AppendColumn(col) renderer, err = gtk.CellRendererTextNew() if err != nil { log.Fatal(err) } renderer.Set("editable", true) renderer.Set("editable-set", true) renderer.Connect("edited", func(_ *glib.Object, path, text string) { iter, err := store.GetIterFromString(path) if err == nil { // TODO(jrick): verify this is a valid address store.Set(iter, []int{1}, []interface{}{text}) } }) col, err = gtk.TreeViewColumnNewWithAttribute("Address", renderer, "text", 1) if err != nil { log.Fatal(err) } col.SetMinWidth(350) tv.AppendColumn(col) // put in an example address iter := store.Append() store.Set(iter, []int{0, 1}, []interface{}{"editable label", "01234567890"}) buttons, err := gtk.GridNew() if err != nil { log.Fatal(err) } newAddr, err := gtk.ButtonNewWithLabel("New Address") if err != nil { log.Fatal(err) } newAddr.SetSizeRequest(150, -1) newAddr.Connect("clicked", func() { iter := store.Append() store.Set(iter, []int{0, 1}, []interface{}{"", "new address"}) }) buttons.Add(newAddr) cpyAddr, err := gtk.ButtonNewWithLabel("Copy Address") if err != nil { log.Fatal(err) } cpyAddr.SetSizeRequest(150, -1) cpyAddr.Connect("clicked", func() { sel, err := tv.GetSelection() if err != nil { log.Fatal(err) } var iter gtk.TreeIter if sel.GetSelected(nil, &iter) { val, err := store.GetValue(&iter, 1) if err != nil { log.Fatal(err) } display, err := gdk.DisplayGetDefault() if err != nil { log.Fatal(err) } clipboard, err := gtk.ClipboardGetForDisplay( display, gdk.SELECTION_CLIPBOARD) if err != nil { log.Fatal(err) } primary, err := gtk.ClipboardGetForDisplay( display, gdk.SELECTION_PRIMARY) if err != nil { log.Fatal(err) } s, _ := val.GetString() clipboard.SetText(s) primary.SetText(s) } }) buttons.Add(cpyAddr) grid.Add(buttons) return &grid.Container.Widget }
func createEncryptionDialog() (*gtk.Dialog, error) { dialog, err := gtk.DialogNew() if err != nil { return nil, err } dialog.SetTitle("Encrypt wallet") dialog.AddButton("_OK", gtk.RESPONSE_OK) dialog.AddButton("_Cancel", gtk.RESPONSE_CANCEL) grid, err := gtk.GridNew() if err != nil { return nil, err } grid.SetHExpand(true) grid.SetVExpand(true) b, err := dialog.GetContentArea() if err != nil { return nil, err } b.Add(grid) b.SetHExpand(true) b.SetVExpand(true) l, err := gtk.LabelNew("") if err != nil { return nil, err } l.SetMarkup(encryptMessage) l.SetHExpand(true) l.SetVExpand(true) l.SetHAlign(gtk.ALIGN_START) grid.Attach(l, 0, 0, 2, 1) l, err = gtk.LabelNew("New passphrase") if err != nil { return nil, err } grid.Attach(l, 0, 1, 1, 1) passphrase, err := gtk.EntryNew() if err != nil { return nil, err } passphrase.SetVisibility(false) passphrase.SetHExpand(true) passphrase.Connect("activate", func() { dialog.Emit("response", gtk.RESPONSE_OK, nil) }) grid.Attach(passphrase, 1, 1, 1, 1) l, err = gtk.LabelNew("Repeat new passphrase") if err != nil { return nil, err } l.SetVExpand(true) l.SetVAlign(gtk.ALIGN_START) grid.Attach(l, 0, 2, 1, 1) repeated, err := gtk.EntryNew() if err != nil { return nil, err } repeated.SetVisibility(false) repeated.SetVExpand(true) repeated.SetVAlign(gtk.ALIGN_START) repeated.Connect("activate", func() { dialog.Emit("response", gtk.RESPONSE_OK, nil) }) grid.Attach(repeated, 1, 2, 1, 1) dialog.SetTransientFor(mainWindow) dialog.SetPosition(gtk.WIN_POS_CENTER_ON_PARENT) dialog.ShowAll() dialog.Connect("response", func(_ *glib.Object, rt gtk.ResponseType) { switch rt { case gtk.RESPONSE_OK: pStr, err := passphrase.GetText() if err != nil { log.Print(err) return } rStr, err := repeated.GetText() if err != nil { log.Print(err) return } if pStr == rStr { // use the passphrase, encrypt wallet... dialog.Destroy() } else { msg := "The supplied passphrases do not match." mDialog := gtk.MessageDialogNew(dialog, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg) mDialog.SetTitle("Wallet encryption failed") mDialog.Run() mDialog.Destroy() } case gtk.RESPONSE_CANCEL: dialog.Destroy() } }) return dialog, nil }
func main() { gtk.Init(nil) win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL) if err != nil { log.Fatal("Unable to create window:", err) } win.SetTitle("UDP client v0.0.1") win.Connect("destroy", func() { gtk.MainQuit() }) var c client.Client answers := make(chan string) grid, err := gtk.GridNew() if err != nil { log.Fatal("Unable to create grid:", err) } messageHistory, err := gtk.TextViewNew() if err != nil { log.Fatal("Unable to create TextView:", err) } grid.Attach(messageHistory, 0, 0, 4, 1) messageEntry, err := gtk.EntryNew() if err != nil { log.Fatal("Unable to create entry:", err) } grid.Attach(messageEntry, 0, 1, 1, 1) privateEntry, err := gtk.EntryNew() if err != nil { log.Fatal("Unable to create entry:", err) } grid.Attach(privateEntry, 1, 1, 1, 1) sendButton, err := gtk.ButtonNewWithLabel("Send") if err != nil { log.Fatal("Unable to create button:", err) } sendButton.Connect("clicked", func(btn *gtk.Button) { lbl, _ := btn.GetLabel() if lbl != "Send" { return } log.Print(lbl) msg, _ := messageEntry.GetText() log.Print(msg) c.Message(msg) }) grid.Attach(sendButton, 0, 2, 1, 1) privateButton, err := gtk.ButtonNewWithLabel("Private") if err != nil { log.Fatal("Unable to create button:", err) } privateButton.Connect("clicked", func(btn *gtk.Button) { lbl, _ := btn.GetLabel() if lbl != "Private" { return } log.Print(lbl) private, _ := privateEntry.GetText() log.Print(private) if private != "" { msg, _ := messageEntry.GetText() log.Print(msg) c.Private(private, msg) } }) grid.Attach(privateButton, 1, 2, 1, 1) listButton, err := gtk.ButtonNewWithLabel("List") if err != nil { log.Fatal("Unable to create button:", err) } listButton.Connect("clicked", func(btn *gtk.Button) { lbl, _ := btn.GetLabel() if lbl != "List" { return } log.Print(lbl) c.List() log.Print(lbl) }) grid.Attach(listButton, 2, 1, 1, 1) leaveButton, err := gtk.ButtonNewWithLabel("Leave") if err != nil { log.Fatal("Unable to create button:", err) } leaveButton.Connect("clicked", func(btn *gtk.Button) { lbl, _ := btn.GetLabel() if lbl != "Leave" { return } log.Print(lbl) c.Leave() os.Exit(0) }) grid.Attach(leaveButton, 3, 1, 1, 1) win.Add(grid) // Set the default window size. win.SetDefaultSize(400, 600) // Recursively show all widgets contained in this window. win.ShowAll() port, _ := strconv.Atoi(os.Args[3]) c.Init(os.Args[1], os.Args[2], port, answers) go printMessages(messageHistory, answers) go c.Answer() c.Register() gtk.Main() }
func main() { gtk.Init(nil) win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL) if err != nil { log.Fatal("Unable to create window:", err) } win.SetTitle("Grid Example") win.Connect("destroy", func() { gtk.MainQuit() }) // Create a new grid widget to arrange child widgets grid, err := gtk.GridNew() if err != nil { log.Fatal("Unable to create grid:", err) } // gtk.Grid embeds an Orientable struct to simulate the GtkOrientable // GInterface. Set the orientation from the default horizontal to // vertical. grid.SetOrientation(gtk.ORIENTATION_VERTICAL) // Create some widgets to put in the grid. lab, err := gtk.LabelNew("Just a label") if err != nil { log.Fatal("Unable to create label:", err) } btn, err := gtk.ButtonNewWithLabel("Button with label") if err != nil { log.Fatal("Unable to create button:", err) } entry, err := gtk.EntryNew() if err != nil { log.Fatal("Unable to create entry:", err) } spnBtn, err := gtk.SpinButtonNewWithRange(0.0, 1.0, 0.001) if err != nil { log.Fatal("Unable to create spin button:", err) } nb, err := gtk.NotebookNew() if err != nil { log.Fatal("Unable to create notebook:", err) } // Calling (*gtk.Container).Add() with a gtk.Grid will add widgets next // to each other, in the order they were added, to the right side of the // last added widget when the grid is in a horizontal orientation, and // at the bottom of the last added widget if the grid is in a vertial // orientation. Using a grid in this manner works similar to a gtk.Box, // but unlike gtk.Box, a gtk.Grid will respect its child widget's expand // and margin properties. grid.Add(btn) grid.Add(lab) grid.Add(entry) grid.Add(spnBtn) // Widgets may also be added by calling (*gtk.Grid).Attach() to specify // where to place the widget in the grid, and optionally how many rows // and columns to span over. // // Additional rows and columns are automatically added to the grid as // necessary when new widgets are added with (*gtk.Container).Add(), or, // as shown in this case, using (*gtk.Grid).Attach(). // // In this case, a notebook is added beside the widgets inserted above. // The notebook widget is inserted with a left position of 1, a top // position of 1 (starting at the same vertical position as the button), // a width of 1 column, and a height of 2 rows (spanning down to the // same vertical position as the entry). // // This example also demonstrates how not every area of the grid must // contain a widget. In particular, the area to the right of the label // and the right of spin button have contain no widgets. grid.Attach(nb, 1, 1, 1, 2) nb.SetHExpand(true) nb.SetVExpand(true) // Add a child widget and tab label to the notebook so it renders. nbChild, err := gtk.LabelNew("Notebook content") if err != nil { log.Fatal("Unable to create button:", err) } nbTab, err := gtk.LabelNew("Tab label") if err != nil { log.Fatal("Unable to create label:", err) } nb.AppendPage(nbChild, nbTab) // Add the grid to the window, and show all widgets. win.Add(grid) win.ShowAll() gtk.Main() }
func createRecipient(rmFn func(*glib.Object, *recipient)) *recipient { ret := new(recipient) ret.n = recipients.Len() grid, err := gtk.GridNew() if err != nil { log.Fatal(err) } ret.Widget = grid.Container.Widget l, err := gtk.LabelNew("Pay To:") if err != nil { log.Fatal(err) } grid.Attach(l, 0, 0, 1, 1) l, err = gtk.LabelNew("Amount:") if err != nil { log.Fatal(err) } grid.Attach(l, 0, 1, 1, 1) payTo, err := gtk.EntryNew() if err != nil { log.Fatal(err) } payTo.SetHExpand(true) ret.payTo = payTo grid.Attach(payTo, 1, 0, 1, 1) remove, err := gtk.ButtonNew() if err != nil { log.Fatal(err) } img, err := gtk.ImageNewFromIconName("_Delete", gtk.ICON_SIZE_MENU) if err != nil { log.Fatal(err) } remove.SetImage(img) remove.SetTooltipText("Remove this recipient") remove.Connect("clicked", rmFn, ret) grid.Attach(remove, 2, 0, 1, 1) // TODO(jrick): Label doesn't do anything currently, so don't add // to gui. /* l, err = gtk.LabelNew("Label:") if err != nil { log.Fatal(err) } grid.Attach(l, 0, 1, 1, 1) label, err := gtk.EntryNew() if err != nil { log.Fatal(err) } label.SetHExpand(true) ret.label = label grid.Attach(label, 1, 1, 2, 1) */ amounts, err := gtk.GridNew() if err != nil { log.Fatal(err) } amount, err := gtk.SpinButtonNewWithRange(0, 21000000, 0.00000001) if err != nil { log.Fatal(err) } amount.SetHAlign(gtk.ALIGN_START) ret.amount = amount amounts.Add(amount) ls, err := gtk.ListStoreNew(glib.TYPE_STRING) if err != nil { log.Fatal(err) } iter := ls.Append() choices := []string{"BTC", "mBTC", "μBTC"} s := make([]interface{}, len(choices)) for i, v := range choices { s[i] = v } if err := ls.Set(iter, []int{0}, []interface{}{"BTC"}); err != nil { fmt.Println(err) } iter = ls.Append() if err := ls.Set(iter, []int{0}, []interface{}{"mBTC"}); err != nil { fmt.Println(err) } iter = ls.Append() if err := ls.Set(iter, []int{0}, []interface{}{"μBTC"}); err != nil { fmt.Println(err) } // TODO(jrick): add back when this works. /* combo, err := gtk.ComboBoxNewWithModel(ls) if err != nil { log.Fatal(err) } cell, err := gtk.CellRendererTextNew() if err != nil { log.Fatal(err) } combo.PackStart(cell, true) combo.AddAttribute(cell, "text", 0) combo.SetActive(0) combo.Connect("changed", func() { val := amount.GetValue() fmt.Println(val) switch combo.GetActive() { case 0: fmt.Println("btc") case 1: fmt.Println("mbtc") case 2: fmt.Println("ubtc") } }) ret.combo = combo amounts.Add(combo) */ l, err = gtk.LabelNew("BTC") if err != nil { log.Fatal(err) } amounts.Add(l) grid.Attach(amounts, 1, 1, 1, 1) return ret }
// CreateTutorialDialog opens a tutorial dialog explaining usage for a // first-time user. If appWindow is non-nil, it will be used as the // parent window of the dialog. If nil, the tutorial dialog will open as // a top-level window and a new application main window will be created // and opened after the final tutorial message is shown. func CreateTutorialDialog(appWindow *gtk.Window) (*gtk.Dialog, error) { d, err := gtk.DialogNew() if err != nil { return nil, err } d.SetTitle("btcgui First Start Tutorial") box, err := d.GetContentArea() if err != nil { return nil, err } grid, err := gtk.GridNew() if err != nil { return nil, err } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) box.Add(grid) d.SetDefaultGeometry(500, 100) if appWindow != nil { d.SetTransientFor(appWindow) d.SetPosition(gtk.WIN_POS_CENTER_ON_PARENT) } else { d.Connect("destroy", func() { go StartMainApplication() }) d.SetPosition(gtk.WIN_POS_CENTER) } // Add a notebook and tab for each dialog message. nb, err := gtk.NotebookNew() if err != nil { return nil, err } // Because the labels added below will wrap and their final minimum // heights and widths will be absurdly large, first give a size request // and show the notebook (allocating space for the requested size). // This will make text wrapping labels size nicely inside the notebook. nb.SetSizeRequest(500, 100) nb.Show() // Create messages and append each in a notebook page. for _, msg := range dialogMessages { lbl, err := gtk.LabelNew("") if err != nil { return nil, err } lbl.SetMarkup(msg) lbl.SetLineWrap(true) lbl.Show() lbl.SetAlignment(0, 0) nb.AppendPage(lbl, nil) } nb.SetShowTabs(false) grid.Add(nb) prevPage, err := d.AddButton("_Previous", gtk.RESPONSE_NONE) if err != nil { return nil, err } prevPage.SetSensitive(false) nextPage, err := d.AddButton("_Next", gtk.RESPONSE_NONE) if err != nil { return nil, err } prevPage.Connect("clicked", func() { nb.PrevPage() pagen := nb.GetCurrentPage() if pagen == 0 { prevPage.SetSensitive(false) } nextPage.SetSensitive(true) }) nextPage.Connect("clicked", func() { nb.NextPage() pagen := nb.GetCurrentPage() if pagen == len(dialogMessages)-1 { nextPage.SetSensitive(false) } prevPage.SetSensitive(true) }) _, err = d.AddButton("_Close", gtk.RESPONSE_CLOSE) if err != nil { return nil, err } d.Connect("response", func(_ *glib.Object, rt gtk.ResponseType) { if rt == gtk.RESPONSE_CLOSE { // Using w.Close() would be nice but it needs GTK 3.10. d.Hide() d.Destroy() } }) return d, nil }
func windowWidget() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal("Unable to create grid:", err) } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) entry, err := gtk.EntryNew() if err != nil { log.Fatal("Unable to create entry:", err) } s, _ := entry.GetText() label, err := gtk.LabelNew(s) if err != nil { log.Fatal("Unable to create label:", err) } grid.Add(entry) entry.SetHExpand(true) grid.AttachNextTo(label, entry, gtk.POS_RIGHT, 1, 1) label.SetHExpand(true) // Connects this entry's "activate" signal (which is emitted whenever // Enter is pressed when the Entry is activated) to an anonymous // function that gets the current text of the entry and sets the text of // the label beside it with it. Unlike with native GTK callbacks, // (*glib.Object).Connect() supports closures. In this example, this is // demonstrated by using the label variable. Without closures, a // pointer to the label would need to be passed in as user data // (demonstrated in the next example). entry.Connect("activate", func() { s, _ := entry.GetText() label.SetText(s) }) sb, err := gtk.SpinButtonNewWithRange(0, 1, 0.1) if err != nil { log.Fatal("Unable to create spin button:", err) } pb, err := gtk.ProgressBarNew() if err != nil { log.Fatal("Unable to create progress bar:", err) } grid.Add(sb) sb.SetHExpand(true) grid.AttachNextTo(pb, sb, gtk.POS_RIGHT, 1, 1) label.SetHExpand(true) // Pass in a ProgressBar and the target SpinButton as user data rather // than using the sb and pb variables scoped to the anonymous func. // This can be useful when passing in a closure that has already been // generated, but when you still wish to connect the callback with some // variables only visible in this scope. sb.Connect("value-changed", func(obj *glib.Object, pb *gtk.ProgressBar) { sb := >k.SpinButton{gtk.Entry{gtk.Widget{ glib.InitiallyUnowned{obj}}}} pb.SetFraction(sb.GetValue() / 1) }, pb) label, err = gtk.LabelNew("") if err != nil { log.Fatal("Unable to create label:", err) } s = "Hyperlink to <a href=\"https://www.cyphertite.com/\">Cyphertite</a> for your clicking pleasure" label.SetMarkup(s) grid.AttachNextTo(label, sb, gtk.POS_BOTTOM, 2, 1) // Some GTK callback functions require arguments, such as the // 'gchar *uri' argument of GtkLabel's "activate-link" signal. To use // these arguments, pass in a *glib.CallbackContext as an argument, and // access by calling (*glib.CallbackContext).Arg(n) for the nth // argument. label.Connect("activate-link", func(_ *glib.Object, uri string) { fmt.Println("you clicked a link to:", uri) }) return &grid.Container.Widget }
func createTxLabel(attr *TxAttributes) (*gtk.Widget, error) { grid, err := gtk.GridNew() if err != nil { return nil, err } grid.SetHExpand(true) var amtLabel *gtk.Label var description *gtk.Label var icon *gtk.Image switch attr.Direction { case Send: amtLabel, err = gtk.LabelNew(amountStr(attr.Amount)) if err != nil { return nil, err } description, err = gtk.LabelNew(fmt.Sprintf("Send (%s)", attr.Address)) if err != nil { return nil, err } icon, err = gtk.ImageNewFromIconName("go-next", gtk.ICON_SIZE_SMALL_TOOLBAR) if err != nil { return nil, err } case Recv: amtLabel, err = gtk.LabelNew(amountStr(attr.Amount)) if err != nil { return nil, err } description, err = gtk.LabelNew(fmt.Sprintf("Receive (%s)", attr.Address)) if err != nil { return nil, err } icon, err = gtk.ImageNewFromIconName("go-previous", gtk.ICON_SIZE_SMALL_TOOLBAR) if err != nil { return nil, err } } grid.Attach(icon, 0, 0, 2, 2) grid.Attach(description, 2, 1, 2, 1) description.SetHAlign(gtk.ALIGN_START) grid.Attach(amtLabel, 3, 0, 1, 1) amtLabel.SetHAlign(gtk.ALIGN_END) amtLabel.SetHExpand(true) date, err := gtk.LabelNew(attr.Date.Format("Jan 2, 2006 at 3:04 PM")) if err != nil { return nil, err } grid.Attach(date, 2, 0, 1, 1) date.SetHAlign(gtk.ALIGN_START) grid.SetHAlign(gtk.ALIGN_FILL) return &grid.Container.Widget, nil }
// createUnlockDialog creates a dialog to enter a passphrase and unlock // an encrypted wallet. If an OK response is received, the passphrase will // be used to attempt a wallet unlock. // // If success is non-nil, the caller may pass in a channel to receive a // notification for whether the unlock was successful. If the dialog is // closed without sending a request to btcwallet and the channel is // non-nil, the channel is closed. func createUnlockDialog(reason *UnlockText, success chan bool) (*gtk.Dialog, error) { dialog, err := gtk.DialogNew() if err != nil { return nil, err } dialog.SetTitle(reason.Title) dialog.AddButton("_OK", gtk.RESPONSE_OK) dialog.AddButton("_Cancel", gtk.RESPONSE_CANCEL) grid, err := gtk.GridNew() if err != nil { return nil, err } grid.SetHExpand(true) grid.SetVExpand(true) b, err := dialog.GetContentArea() if err != nil { return nil, err } b.Add(grid) b.SetHExpand(true) b.SetVExpand(true) lbl, err := gtk.LabelNew(reason.Message) if err != nil { return nil, err } grid.Attach(lbl, 0, 0, 2, 1) lbl, err = gtk.LabelNew("Passphrase") if err != nil { return nil, err } grid.Attach(lbl, 0, 1, 1, 1) passphrase, err := gtk.EntryNew() if err != nil { return nil, err } passphrase.SetVisibility(false) passphrase.SetHExpand(true) passphrase.SetVExpand(true) passphrase.Connect("activate", func() { dialog.Emit("response", gtk.RESPONSE_OK, nil) }) grid.Attach(passphrase, 1, 1, 1, 1) lbl, err = gtk.LabelNew("Timeout (s)") if err != nil { return nil, err } grid.Attach(lbl, 0, 2, 1, 1) timeout, err := gtk.SpinButtonNewWithRange(0, float64(1<<64-1), 1) if err != nil { return nil, err } timeout.SetValue(60) timeout.Connect("activate", func() { dialog.Emit("response", gtk.RESPONSE_OK, nil) }) grid.Attach(timeout, 1, 2, 1, 1) dialog.SetTransientFor(mainWindow) dialog.SetPosition(gtk.WIN_POS_CENTER_ON_PARENT) dialog.ShowAll() dialog.Connect("response", func(_ *glib.Object, rt gtk.ResponseType) { switch rt { case gtk.RESPONSE_OK: pStr, err := passphrase.GetText() if err != nil { log.Print(err) return } timeoutSecs := timeout.GetValueAsInt() go func() { triggers.unlockWallet <- &UnlockParams{ pStr, int64(timeoutSecs), } if ok := <-triggerReplies.unlockSuccessful; ok { if success != nil { success <- true } glib.IdleAdd(func() { dialog.Destroy() }) } else { if success != nil { success <- false } glib.IdleAdd(func() { mDialog := gtk.MessageDialogNew(dialog, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Wallet decryption failed.") mDialog.SetTitle("Wallet decryption failed") mDialog.Run() mDialog.Destroy() }) } }() case gtk.RESPONSE_CANCEL: if success != nil { close(success) } dialog.Destroy() } }) return dialog, nil }
func createTxFeeDialog() (*gtk.Dialog, error) { dialog, err := gtk.DialogNew() if err != nil { return nil, err } dialog.SetTitle("Set Transaction Fee") dialog.AddButton("_OK", gtk.RESPONSE_OK) dialog.AddButton("_Cancel", gtk.RESPONSE_CANCEL) grid, err := gtk.GridNew() if err != nil { return nil, err } grid.SetHExpand(true) grid.SetVExpand(true) grid.SetOrientation(gtk.ORIENTATION_VERTICAL) b, err := dialog.GetContentArea() if err != nil { return nil, err } b.Add(grid) b.SetHExpand(true) b.SetVExpand(true) l, err := gtk.LabelNew(txFeeMessage) if err != nil { return nil, err } l.SetHExpand(true) l.SetVExpand(true) l.SetHAlign(gtk.ALIGN_START) grid.Add(l) spinb, err := gtk.SpinButtonNewWithRange(0, 21000000, 0.00000001) if err != nil { return nil, err } grid.Add(spinb) dialog.SetTransientFor(mainWindow) dialog.SetPosition(gtk.WIN_POS_CENTER_ON_PARENT) dialog.ShowAll() dialog.Connect("response", func(_ *glib.Object, rt gtk.ResponseType) { switch rt { case gtk.RESPONSE_OK: fee := spinb.GetValue() go func() { triggers.setTxFee <- fee if err := <-triggerReplies.setTxFeeErr; err != nil { d := errorDialog("Error setting transaction fee:", err.Error()) d.Run() d.Destroy() } else { glib.IdleAdd(func() { dialog.Destroy() }) } }() case gtk.RESPONSE_CANCEL: dialog.Destroy() } }) return dialog, nil }
func windowWidget() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal("Unable to create grid:", err) } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) // Just as a demonstration, we create and destroy a Label without ever // adding it to a container. In native GTK, this would result in a // memory leak, since gtk_widget_destroy() will not deallocate any // memory when passed a GtkWidget with a floating reference. // // gotk3 handles this situation by always sinking floating references // of any struct type embedding a glib.InitiallyUnowned, and by setting // a finalizer to unreference the object when Go has lost scope of the // variable. Due to this design, widgets may be allocated freely // without worrying about handling memory incorrectly. // // The following code is not entirely useful (except to demonstrate // this point), but it is also not "incorrect" as the C equivalent // would be. unused, err := gtk.LabelNew("This label is never used") if err != nil { // Calling Destroy() is also unnecessary in this case. The // memory will still be freed with or without calling it. unused.Destroy() } sw, err := gtk.ScrolledWindowNew(nil, nil) if err != nil { log.Fatal("Unable to create scrolled window:", err) } grid.Attach(sw, 0, 0, 2, 1) sw.SetHExpand(true) sw.SetVExpand(true) labelsGrid, err := gtk.GridNew() if err != nil { log.Fatal("Unable to create grid:", err) } labelsGrid.SetOrientation(gtk.ORIENTATION_VERTICAL) sw.Add(labelsGrid) labelsGrid.SetHExpand(true) insertBtn, err := gtk.ButtonNewWithLabel("Add a label") if err != nil { log.Fatal("Unable to create button:", err) } removeBtn, err := gtk.ButtonNewWithLabel("Remove a label") if err != nil { log.Fatal("Unable to create button:", err) } nLabels := 1 insertBtn.Connect("clicked", func() { var s string if nLabels == 1 { s = fmt.Sprintf("Inserted %d label.", nLabels) } else { s = fmt.Sprintf("Inserted %d labels.", nLabels) } label, err := gtk.LabelNew(s) if err != nil { log.Print("Unable to create label:", err) return } labelList.PushBack(label) labelsGrid.Add(label) label.SetHExpand(true) labelsGrid.ShowAll() nLabels++ }) removeBtn.Connect("clicked", func() { e := labelList.Front() if e == nil { log.Print("Nothing to remove") return } lab, ok := labelList.Remove(e).(*gtk.Label) if !ok { log.Print("Element to remove is not a *gtk.Label") return } // (*Widget).Destroy() breaks this label's reference with all // other objects (in this case, the Grid container it was added // to). lab.Destroy() // At this point, only Go retains a reference to the GtkLabel. // When the lab variable goes out of scope when this function // returns, at the next garbage collector run, a finalizer will // be run to perform the final unreference and free the widget. }) grid.Attach(insertBtn, 0, 1, 1, 1) grid.Attach(removeBtn, 1, 1, 1, 1) return &grid.Container.Widget }
// CreateWindow creates the toplevel window for the GUI. func CreateWindow() (*gtk.Window, error) { var err error mainWindow, err = gtk.WindowNew(gtk.WINDOW_TOPLEVEL) if err != nil { return nil, err } mainWindow.SetTitle("btcgui") mainWindow.Connect("destroy", func() { gtk.MainQuit() }) grid, err := gtk.GridNew() if err != nil { return nil, err } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) grid.Add(createMenuBar()) notebook, err := gtk.NotebookNew() if err != nil { return nil, err } notebook.SetHExpand(true) notebook.SetVExpand(true) grid.Add(notebook) l, err := gtk.LabelNew("Overview") if err != nil { return nil, err } notebook.AppendPage(createOverview(), l) l, err = gtk.LabelNew("Send Coins") if err != nil { return nil, err } notebook.AppendPage(createSendCoins(), l) l, err = gtk.LabelNew("Receive Coins") if err != nil { return nil, err } notebook.AppendPage(createRecvCoins(), l) l, err = gtk.LabelNew("Transactions") if err != nil { log.Fatal(err) } notebook.AppendPage(createTransactions(), l) // TODO(jrick): Add back when address book is implemented. /* l, err = gtk.LabelNew("Address Book") if err != nil { log.Fatal(err) } notebook.AppendPage(createAddrBook(), l) */ grid.Add(createStatusbar()) mainWindow.Add(grid) mainWindow.SetDefaultGeometry(800, 600) return mainWindow, nil }
func createRecvCoins() *gtk.Widget { store, err := gtk.ListStoreNew(glib.TYPE_STRING, glib.TYPE_STRING) if err != nil { log.Fatal(err) } RecvCoins.Store = store tv, err := gtk.TreeViewNewWithModel(store) if err != nil { log.Fatal(err) } RecvCoins.Treeview = tv renderer, err := gtk.CellRendererTextNew() if err != nil { log.Fatal(err) } renderer.Set("editable", true) renderer.Set("editable-set", true) renderer.Connect("edited", func(_ *glib.Object, path, text string) { iter, err := store.GetIterFromString(path) if err == nil { store.Set(iter, []int{0}, []interface{}{text}) } }) col, err := gtk.TreeViewColumnNewWithAttribute("Label", renderer, "text", 0) if err != nil { log.Fatal(err) } col.SetExpand(true) tv.AppendColumn(col) cr, err := gtk.CellRendererTextNew() if err != nil { log.Fatal(err) } col, err = gtk.TreeViewColumnNewWithAttribute("Address", cr, "text", 1) if err != nil { log.Fatal(err) } col.SetMinWidth(350) tv.AppendColumn(col) newAddr, err := gtk.ButtonNewWithLabel("New Address") if err != nil { log.Fatal(err) } newAddr.SetSizeRequest(150, -1) newAddr.Connect("clicked", func() { go func() { triggers.newAddr <- 1 reply := <-triggerReplies.newAddr if err, ok := reply.(error); ok { glib.IdleAdd(func() { mDialog := errorDialog("New address generation failed", err.Error()) mDialog.Run() mDialog.Destroy() }) } else if addr, ok := reply.(string); ok { glib.IdleAdd(func() { iter := RecvCoins.Store.Append() RecvCoins.Store.Set(iter, []int{0, 1}, []interface{}{"", addr}) }) } }() }) newAddr.SetSensitive(false) RecvCoins.NewAddrBtn = newAddr buttons, err := gtk.GridNew() if err != nil { log.Fatal(err) } buttons.Add(newAddr) cpyAddr, err := gtk.ButtonNewWithLabel("Copy Address") if err != nil { log.Fatal(err) } cpyAddr.SetSizeRequest(150, -1) cpyAddr.Connect("clicked", func() { sel, err := tv.GetSelection() if err != nil { log.Fatal(err) } var iter gtk.TreeIter if sel.GetSelected(nil, &iter) { val, err := store.GetValue(&iter, 1) if err != nil { log.Fatal(err) } display, err := gdk.DisplayGetDefault() if err != nil { log.Fatal(err) } clipboard, err := gtk.ClipboardGetForDisplay( display, gdk.SELECTION_CLIPBOARD) if err != nil { log.Fatal(err) } primary, err := gtk.ClipboardGetForDisplay( display, gdk.SELECTION_PRIMARY) if err != nil { log.Fatal(err) } s, _ := val.GetString() clipboard.SetText(s) primary.SetText(s) } }) buttons.Add(cpyAddr) sw, err := gtk.ScrolledWindowNew(nil, nil) if err != nil { log.Fatal(err) } sw.Add(tv) sw.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.SetHExpand(true) sw.SetVExpand(true) grid, err := gtk.GridNew() if err != nil { log.Fatal(err) } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) grid.Add(sw) grid.Add(buttons) return &grid.Container.Widget }
func createSendCoins() *gtk.Widget { grid, err := gtk.GridNew() if err != nil { log.Fatal(err) } grid.SetOrientation(gtk.ORIENTATION_VERTICAL) sw, err := gtk.ScrolledWindowNew(nil, nil) if err != nil { log.Fatal(err) } sw.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.SetHExpand(true) sw.SetVExpand(true) grid.Add(sw) entriesGrid, err := gtk.GridNew() if err != nil { log.Fatal(err) } SendCoins.EntryGrid = entriesGrid entriesGrid.SetOrientation(gtk.ORIENTATION_VERTICAL) sw.Add(entriesGrid) insertSendEntries(entriesGrid) bot, err := gtk.GridNew() if err != nil { log.Fatal(err) } btn, err := gtk.ButtonNewWithLabel("Add Recipient") if err != nil { log.Fatal(err) } btn.SetSizeRequest(150, -1) btn.Connect("clicked", func() { insertSendEntries(entriesGrid) }) bot.Add(btn) l, err := gtk.LabelNew("Balance: ") if err != nil { log.Fatal(err) } bot.Add(l) SendCoins.Balance = l submitBtn, err := gtk.ButtonNewWithLabel("Send") if err != nil { log.Fatal(err) } submitBtn.SetSizeRequest(150, -1) submitBtn.SetHAlign(gtk.ALIGN_END) submitBtn.SetHExpand(true) submitBtn.SetSensitive(false) submitBtn.Connect("clicked", func() { sendTo := make(map[string]float64) for e := recipients.Front(); e != nil; e = e.Next() { r := e.Value.(*recipient) // Get and validate address addr, err := r.payTo.GetText() if err != nil { d := errorDialog("Error getting payment address", err.Error()) d.Run() d.Destroy() return } _, net, err := btcutil.DecodeAddress(addr) if err != nil { d := errorDialog("Invalid payment address", fmt.Sprintf("'%v' is not a valid payment address", addr)) d.Run() d.Destroy() return } switch net { case btcwire.MainNet: if !cfg.MainNet { d := errorDialog("Invalid payment address", fmt.Sprintf("'%v' is a mainnet address", addr)) d.Run() d.Destroy() return } case btcwire.TestNet3: if cfg.MainNet { d := errorDialog("Invalid payment address", fmt.Sprintf("'%v' is a testnet address", addr)) d.Run() d.Destroy() return } } // Get amount and units and convert to float64 amt := r.amount.GetValue() // Combo box isn't used right now. /* switch r.combo.GetActive() { case 0: // BTC // nothing case 1: // mBTC amt /= 1000 case 2: // uBTC amt /= 1000000 } */ sendTo[addr] = amt } go txSenderAndReplyListener(sendTo) }) SendCoins.SendBtn = submitBtn bot.Add(submitBtn) grid.Add(bot) return &grid.Container.Widget }