bsnes/higan/phoenix/gtk/widget/list-view.cpp

218 lines
8.1 KiB
C++
Raw Normal View History

static void ListView_activate(ListView *self) {
if(self->onActivate) self->onActivate();
}
static void ListView_change(ListView *self) {
if(self->state.selected == false || self->state.selection != self->selection()) {
self->state.selected = true;
self->state.selection = self->selection();
if(self->onChange) self->onChange();
}
}
static void ListView_toggle(GtkCellRendererToggle *cell, gchar *path, ListView *self) {
unsigned row = decimal(path);
self->setChecked(row, !self->checked(row));
if(self->onToggle) self->onToggle(row);
}
void pListView::append(const lstring &text) {
GtkTreeIter iter;
gtk_list_store_append(store, &iter);
for(unsigned n = 0; n < text.size(); n++) gtk_list_store_set(store, &iter, 1 + n * 2 + 1, (const char*)text[n], -1);
}
void pListView::autoSizeColumns() {
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(subWidget));
}
bool pListView::checked(unsigned row) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
bool state;
2011-11-17 12:05:35 +00:00
if(gtk_tree_model_get_iter_from_string(model, &iter, string(row)) == false) return false;
gtk_tree_model_get(model, &iter, 0, &state, -1);
return state;
}
void pListView::modify(unsigned row, const lstring &text) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
2011-11-17 12:05:35 +00:00
gtk_tree_model_get_iter_from_string(model, &iter, string(row));
for(unsigned n = 0; n < text.size(); n++) gtk_list_store_set(store, &iter, 1 + n * 2 + 1, (const char*)text[n], -1);
}
void pListView::remove(unsigned row) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
gtk_tree_model_get_iter_from_string(model, &iter, string(row));
gtk_list_store_remove(store, &iter);
}
void pListView::reset() {
listView.state.selected = false;
listView.state.selection = 0;
gtk_list_store_clear(GTK_LIST_STORE(store));
gtk_tree_view_set_model(GTK_TREE_VIEW(subWidget), GTK_TREE_MODEL(store));
//reset gtk_scrolled_window scrollbar position to 0,0 (top-left), as ListView is now empty
gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(gtkWidget), 0);
gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(gtkWidget), 0);
}
bool pListView::selected() {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
2011-11-17 12:05:35 +00:00
return gtk_tree_selection_get_selected(selection, 0, 0);
}
unsigned pListView::selection() {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
2011-11-17 12:05:35 +00:00
if(gtk_tree_selection_get_selected(selection, 0, &iter) == false) return listView.state.selection;
char *path = gtk_tree_model_get_string_from_iter(model, &iter);
unsigned row = decimal(path);
g_free(path);
return row;
}
void pListView::setCheckable(bool checkable) {
gtk_cell_renderer_set_visible(column(0).checkbox, checkable);
}
void pListView::setChecked(unsigned row, bool checked) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
2011-11-17 12:05:35 +00:00
gtk_tree_model_get_iter_from_string(model, &iter, string(row));
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1);
}
void pListView::setHeaderText(const lstring &text) {
destructor();
constructor();
}
void pListView::setHeaderVisible(bool visible) {
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(subWidget), visible);
}
void pListView::setImage(unsigned row, unsigned column, const nall::image &image) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
gtk_tree_model_get_iter_from_string(model, &iter, string(row));
if(image.empty() == false) {
GdkPixbuf *pixbuf = CreatePixbuf(image, true);
gtk_list_store_set(store, &iter, 1 + column * 2, pixbuf, -1);
} else {
gtk_list_store_set(store, &iter, 1 + column * 2, nullptr, -1);
}
}
void pListView::setSelected(bool selected) {
if(selected == false) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
gtk_tree_selection_unselect_all(selection);
} else {
setSelection(listView.state.selection);
}
}
void pListView::setSelection(unsigned row) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
gtk_tree_selection_unselect_all(selection);
GtkTreeIter iter;
2011-11-17 12:05:35 +00:00
if(gtk_tree_model_get_iter_from_string(model, &iter, string(row)) == false) return;
gtk_tree_selection_select_iter(selection, &iter);
Update to v088r10 release. byuu says: ethos is going to be absolutely amazing. You guys are in for a treat :D I'm impressing the hell out of myself with how well-structured this code is, it's allowing me to do amazing new things. Just a small sampling of what's in store (and already implemented): The file browser will display folders as "[ folder name ]", and cartridge folders as "Game Name" (no extension, no /) [icons would be nicer, but well ... phoenix.] Folders are sorted above cartridge folders. Cartridge folders for other systems do not show up in the list. Not only are unique paths stored for each image type, your position in the list is saved across runs. Some voodoo was added to GTK+ so that all targets even scroll directly to that item when you open the list. Load->System->Enter restarts your last game. That sounds really simple and obvious, but it makes an -incredible- difference. Didn't realize it until I tried an implementation of it, wow. The input mapping list now lets you bind as many hotkeys as you want to any given input. So SFC::Port1::Joypad::B = Keyboard::Z or Joypad::Button1 ... no need to remap everything to switch between keyboard and joypad. Either one activates the key. There is a separate Hotkeys tab now. This should hopefully end the confusion about how to remap hotkeys that users experience. Hotkeys are different, too. Instead of OR logic, they use AND logic. So Fullscreen = Keyboard::Alt and Keyboard::Enter. Both must be pressed to enter the key. This lets you easily implement "super" modifier keys. The actual codebase has new features the old UI never had, and has about ~50% of the old functionality (so far, of course), yet is only ~25% as much code. The entire GUI no longer needs to pull in all the headers for each emulated system. It just needs a small interface header file. Then bind the entire system with exactly **two** lines of code. Everything is dynamically generated for you after that.
2012-04-30 23:43:23 +00:00
//scroll window to selected item
char *path = gtk_tree_model_get_string_from_iter(model, &iter);
GtkTreePath *treePath = gtk_tree_path_new_from_string(path);
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(subWidget), treePath, nullptr, true, 0.5, 0.0);
gtk_tree_path_free(treePath);
g_free(path);
}
void pListView::constructor() {
gtkWidget = gtk_scrolled_window_new(0, 0);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
lstring headerText = listView.state.headerText;
if(headerText.size() == 0) headerText.append(""); //ListView must have at least one column
column.reset();
vector<GType> gtype;
for(auto &text : headerText) {
GtkColumn cell;
cell.label = gtk_label_new(text);
cell.column = gtk_tree_view_column_new();
gtk_tree_view_column_set_resizable(cell.column, true);
gtk_tree_view_column_set_title(cell.column, "");
if(column.size() == 0) { //first column checkbox
cell.checkbox = gtk_cell_renderer_toggle_new();
gtk_tree_view_column_pack_start(cell.column, cell.checkbox, false);
gtk_tree_view_column_set_attributes(cell.column, cell.checkbox, "active", gtype.size(), nullptr);
gtype.append(G_TYPE_BOOLEAN);
g_signal_connect(cell.checkbox, "toggled", G_CALLBACK(ListView_toggle), (gpointer)&listView);
}
cell.icon = gtk_cell_renderer_pixbuf_new();
gtk_tree_view_column_pack_start(cell.column, cell.icon, false);
gtk_tree_view_column_set_attributes(cell.column, cell.icon, "pixbuf", gtype.size(), nullptr);
gtype.append(GDK_TYPE_PIXBUF);
cell.text = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(cell.column, cell.text, false);
gtk_tree_view_column_set_attributes(cell.column, cell.text, "text", gtype.size(), nullptr);
gtype.append(G_TYPE_STRING);
column.append(cell);
}
store = gtk_list_store_newv(gtype.size(), gtype.data());
subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
g_object_unref(G_OBJECT(store));
for(auto &cell : column) {
gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(cell.column), cell.label);
gtk_tree_view_append_column(GTK_TREE_VIEW(subWidget), cell.column);
gtk_widget_show(cell.label);
}
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(subWidget), headerText.size() >= 2); //two or more columns + checkbox column
gtk_tree_view_set_search_column(GTK_TREE_VIEW(subWidget), 2);
g_signal_connect_swapped(G_OBJECT(subWidget), "cursor-changed", G_CALLBACK(ListView_change), (gpointer)&listView);
g_signal_connect_swapped(G_OBJECT(subWidget), "row-activated", G_CALLBACK(ListView_activate), (gpointer)&listView);
gtk_widget_show(subWidget);
setHeaderVisible(listView.state.headerVisible);
setCheckable(listView.state.checkable);
for(auto &text : listView.state.text) append(text);
for(unsigned n = 0; n < listView.state.checked.size(); n++) setChecked(n, listView.state.checked[n]);
if(listView.state.selected) setSelection(listView.state.selection);
autoSizeColumns();
}
void pListView::destructor() {
gtk_widget_destroy(subWidget);
gtk_widget_destroy(gtkWidget);
}
void pListView::orphan() {
destructor();
constructor();
}
void pListView::setFocused() {
gtk_widget_grab_focus(subWidget);
}
void pListView::setFont(const string &font) {
pFont::setFont(gtkWidget, font);
for(auto &cell : column) pFont::setFont(cell.label, font);
}