forked from andlabs/ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
listbox_unix.go
211 lines (181 loc) · 7.55 KB
/
listbox_unix.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// +build !windows,!darwin,!plan9
// 17 february 2014
package ui
import (
"fmt"
"unsafe"
)
/*
GTK+ 3.10 introduces a dedicated GtkListView type for simple listboxes like our Listbox. Unfortunately, since I want to target at least GTK+ 3.4, I need to do things the old, long, and hard way: manually with a GtkTreeView and GtkListStore model.
You are not expected to understand this.
if you must though:
GtkTreeViews are model/view. We use a GtkListStore as a model.
GtkTreeViews also separate selections into another type, but the GtkTreeView creates the selection object for us.
GtkTreeViews can scroll, but do not draw scrollbars or borders; we need to use a GtkScrolledWindow to hold the GtkTreeView to do so. We return the GtkScrolledWindow and get its control out when we want to access the GtkTreeView.
Like with Windows, there's a difference between signle-selection and multi-selection GtkTreeViews when it comes to getting the list of selections that we can exploit. The GtkTreeSelection class hands us an iterator and the model (for some reason). We pull a GtkTreePath out of the iterator, which we can then use to get the indices or text data.
For more information, read
https://developer.gnome.org/gtk3/3.4/TreeWidget.html
http://ubuntuforums.org/showthread.php?t=1208655
http://scentric.net/tutorial/sec-treemodel-remove-row.html
http://gtk.10911.n7.nabble.com/Scrollbars-in-a-GtkTreeView-td58076.html
http://stackoverflow.com/questions/11407447/gtk-treeview-get-current-row-index-in-python (I think; I don't remember if I wound up using this one as a reference or not; I know after that I found the ubuntuforums link above)
and the GTK+ reference documentation.
*/
// #include "gtk_unix.h"
// /* because cgo seems to choke on ... */
// void gtkTreeModelGet(GtkTreeModel *model, GtkTreeIter *iter, gchar **gs)
// {
// /* 0 is the column #; we only have one column here */
// gtk_tree_model_get(model, iter, 0, gs, -1);
// }
// GtkListStore *gtkListStoreNew(void)
// {
// /* 1 column that stores strings */
// return gtk_list_store_new(1, G_TYPE_STRING);
// }
// void gtkListStoreSet(GtkListStore *ls, GtkTreeIter *iter, char *gs)
// {
// /* same parameters as in gtkTreeModelGet() */
// gtk_list_store_set(ls, iter, 0, (gchar *) gs, -1);
// }
// GtkTreeViewColumn *gtkTreeViewColumnNewWithAttributes(GtkCellRenderer *renderer)
// {
// /* "" is the column header; "text" associates the text of the column with column 0 */
// return gtk_tree_view_column_new_with_attributes("", renderer, "text", 0, NULL);
// }
import "C"
func fromgtktreemodel(x *C.GtkTreeModel) *C.GtkWidget {
return (*C.GtkWidget)(unsafe.Pointer(x))
}
func togtktreemodel(what *C.GtkWidget) *C.GtkTreeModel {
return (*C.GtkTreeModel)(unsafe.Pointer(what))
}
func fromgtktreeview(x *C.GtkTreeView) *C.GtkWidget {
return (*C.GtkWidget)(unsafe.Pointer(x))
}
func togtktreeview(what *C.GtkWidget) *C.GtkTreeView {
return (*C.GtkTreeView)(unsafe.Pointer(what))
}
func gListboxNew(multisel bool) *C.GtkWidget {
store := C.gtkListStoreNew()
widget := C.gtk_tree_view_new_with_model((*C.GtkTreeModel)(unsafe.Pointer(store)))
tv := (*C.GtkTreeView)(unsafe.Pointer(widget))
column := C.gtkTreeViewColumnNewWithAttributes(C.gtk_cell_renderer_text_new())
C.gtk_tree_view_column_set_sizing(column, C.GTK_TREE_VIEW_COLUMN_AUTOSIZE)
C.gtk_tree_view_column_set_resizable(column, C.FALSE) // not resizeable by the user; just autoresize
C.gtk_tree_view_append_column(tv, column)
C.gtk_tree_view_set_headers_visible(tv, C.FALSE)
sel := C.GTK_SELECTION_SINGLE
if multisel {
sel = C.GTK_SELECTION_MULTIPLE
}
C.gtk_tree_selection_set_mode(C.gtk_tree_view_get_selection(tv), C.GtkSelectionMode(sel))
scrollarea := C.gtk_scrolled_window_new((*C.GtkAdjustment)(nil), (*C.GtkAdjustment)(nil))
// thanks to jlindgren in irc.gimp.net/#gtk+
C.gtk_scrolled_window_set_shadow_type((*C.GtkScrolledWindow)(unsafe.Pointer(scrollarea)), C.GTK_SHADOW_IN)
C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(scrollarea)), widget)
return scrollarea
}
func gListboxNewSingle() *C.GtkWidget {
return gListboxNew(false)
}
func gListboxNewMulti() *C.GtkWidget {
return gListboxNew(true)
}
func getTreeViewFrom(widget *C.GtkWidget) *C.GtkTreeView {
wid := C.gtk_bin_get_child((*C.GtkBin)(unsafe.Pointer(widget)))
return (*C.GtkTreeView)(unsafe.Pointer(wid))
}
func gListboxText(widget *C.GtkWidget) string {
var model *C.GtkTreeModel
var iter C.GtkTreeIter
var gs *C.gchar
tv := getTreeViewFrom(widget)
sel := C.gtk_tree_view_get_selection(tv)
if !fromgbool(C.gtk_tree_selection_get_selected(sel, &model, &iter)) {
return ""
}
C.gtkTreeModelGet(model, &iter, &gs)
return fromgstr(gs)
}
func gListboxAppend(widget *C.GtkWidget, what string) {
var iter C.GtkTreeIter
tv := getTreeViewFrom(widget)
ls := (*C.GtkListStore)(unsafe.Pointer(C.gtk_tree_view_get_model(tv)))
C.gtk_list_store_append(ls, &iter)
cwhat := C.CString(what)
defer C.free(unsafe.Pointer(cwhat))
C.gtkListStoreSet(ls, &iter, cwhat)
}
func gListboxInsert(widget *C.GtkWidget, index int, what string) {
var iter C.GtkTreeIter
tv := getTreeViewFrom(widget)
ls := (*C.GtkListStore)(unsafe.Pointer(C.gtk_tree_view_get_model(tv)))
C.gtk_list_store_insert(ls, &iter, C.gint(index))
cwhat := C.CString(what)
defer C.free(unsafe.Pointer(cwhat))
C.gtkListStoreSet(ls, &iter, cwhat)
}
func gListboxSelectedMulti(widget *C.GtkWidget) (indices []int) {
var model *C.GtkTreeModel
tv := getTreeViewFrom(widget)
sel := C.gtk_tree_view_get_selection(tv)
rows := C.gtk_tree_selection_get_selected_rows(sel, &model)
defer C.g_list_free_full(rows, C.GDestroyNotify(unsafe.Pointer(C.gtk_tree_path_free)))
// g_list_length() is O(N), but we need the length below, alas
len := C.g_list_length(rows)
if len == 0 {
return nil
}
indices = make([]int, len)
for i := C.guint(0); i < len; i++ {
path := (*C.GtkTreePath)(unsafe.Pointer(rows.data))
idx := C.gtk_tree_path_get_indices(path)
indices[i] = int(*idx)
rows = rows.next
}
return indices
}
func gListboxSelMultiTexts(widget *C.GtkWidget) (texts []string) {
var model *C.GtkTreeModel
var iter C.GtkTreeIter
var gs *C.gchar
tv := getTreeViewFrom(widget)
sel := C.gtk_tree_view_get_selection(tv)
rows := C.gtk_tree_selection_get_selected_rows(sel, &model)
defer C.g_list_free_full(rows, C.GDestroyNotify(unsafe.Pointer(C.gtk_tree_path_free)))
len := C.g_list_length(rows)
if len == 0 {
return nil
}
texts = make([]string, len)
for i := C.guint(0); i < len; i++ {
path := (*C.GtkTreePath)(unsafe.Pointer(rows.data))
if C.gtk_tree_model_get_iter(model, &iter, path) == C.FALSE {
panic("gtk_tree_model_get_iter() failed getting Listbox selected texts; reason unknown")
}
C.gtkTreeModelGet(model, &iter, &gs)
texts[i] = fromgstr(gs)
rows = rows.next
}
return texts
}
func gListboxDelete(widget *C.GtkWidget, index int) {
var iter C.GtkTreeIter
tv := getTreeViewFrom(widget)
ls := (*C.GtkListStore)(unsafe.Pointer(C.gtk_tree_view_get_model(tv)))
if C.gtk_tree_model_iter_nth_child((*C.GtkTreeModel)(unsafe.Pointer(ls)), &iter, (*C.GtkTreeIter)(nil), C.gint(index)) == C.FALSE {
panic(fmt.Errorf("error deleting row %d from GTK+ Listbox: no such index or some other error", index))
}
C.gtk_list_store_remove(ls, &iter)
}
// this is a separate function because Combobox uses it too
func gtkTreeModelListLen(model *C.GtkTreeModel) int {
// "As a special case, if iter is NULL, then the number of toplevel nodes is returned."
return int(C.gtk_tree_model_iter_n_children(model, (*C.GtkTreeIter)(nil)))
}
func gListboxLen(widget *C.GtkWidget) int {
tv := getTreeViewFrom(widget)
model := C.gtk_tree_view_get_model(tv)
return gtkTreeModelListLen(model)
}