Exemplo n.º 1
0
// NewNavigationBar creates a new navigation bar for a HTML page.
func NewNavigationBar() *NavigationBar {
	tb, _ := gtk.ToolbarNew()

	var back, forward, stop, reload *gtk.ToolButton
	buttons := []struct {
		iconName  string
		tooltip   string
		show      bool
		sensitive bool
		button    **gtk.ToolButton
	}{
		{"go-previous", "Go back", true, false, &back},
		{"go-next", "Go forward", true, false, &forward},
		{"process-stop", "Stop loading page", true, true, &stop},
		{"view-refresh", "Reload page", false, true, &reload},
	}
	for i := range buttons {
		b := &buttons[i]
		image, _ := gtk.ImageNewFromIconName(b.iconName, navbarIconSize)
		image.Show()
		button, _ := gtk.ToolButtonNew(image, b.iconName)
		button.SetTooltipText(b.tooltip)
		if b.show {
			button.Show()
		}
		button.SetSensitive(b.sensitive)
		tb.Add(button)
		*b.button = button
	}

	sep, _ := gtk.SeparatorToolItemNew()
	sep.SetDraw(false)
	tb.Add(sep)
	sep.Show()

	uri, _ := gtk.EntryNew()
	uri.SetInputPurpose(gtk.INPUT_PURPOSE_URL)
	uri.SetIconFromIconName(gtk.ENTRY_ICON_PRIMARY, "broken")
	uri.SetIconFromIconName(gtk.ENTRY_ICON_SECONDARY, "non-starred")
	tool, _ := gtk.ToolItemNew()
	tool.Add(uri)
	tool.SetExpand(true)
	tool.ShowAll()
	tb.Add(tool)

	sep, _ = gtk.SeparatorToolItemNew()
	sep.SetDraw(false)
	tb.Add(sep)
	sep.Show()

	search, _ := gtk.SearchEntryNew()
	tool, _ = gtk.ToolItemNew()
	tool.Add(search)
	tool.ShowAll()
	tb.Add(tool)

	return &NavigationBar{tb, back, forward, stop, reload, uri, search}
}
Exemplo n.º 2
0
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 := &gtk.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
}
Exemplo n.º 3
0
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
}
Exemplo n.º 4
0
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
}
Exemplo n.º 5
0
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()
}
Exemplo n.º 6
0
Arquivo: grid.go Projeto: alimy/gotk3
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()
}
Exemplo n.º 7
0
// 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
}
Exemplo n.º 8
0
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
}