2013-03-15 13:11:33 +00:00
|
|
|
namespace phoenix {
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
static void ListView_activate(GtkTreeView* treeView, GtkTreePath* path, GtkTreeViewColumn* column, ListView* self) {
|
|
|
|
char* pathname = gtk_tree_path_to_string(path);
|
|
|
|
unsigned selection = decimal(pathname);
|
|
|
|
g_free(pathname);
|
|
|
|
self->state.selection = selection;
|
2011-02-24 09:27:21 +00:00
|
|
|
if(self->onActivate) self->onActivate();
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
static void ListView_change(GtkTreeView* treeView, ListView* self) {
|
|
|
|
GtkTreeIter iter;
|
|
|
|
if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(treeView), 0, &iter)) return; //should not be possible
|
|
|
|
char* path = gtk_tree_model_get_string_from_iter(gtk_tree_view_get_model(treeView), &iter);
|
|
|
|
unsigned selection = decimal(path);
|
|
|
|
g_free(path);
|
|
|
|
|
|
|
|
if(!self->state.selected || self->state.selection != selection) {
|
2011-02-27 09:05:10 +00:00
|
|
|
self->state.selected = true;
|
2013-11-28 10:29:01 +00:00
|
|
|
self->state.selection = selection;
|
2011-02-27 09:05:10 +00:00
|
|
|
if(self->onChange) self->onChange();
|
|
|
|
}
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
2013-05-02 11:25:45 +00:00
|
|
|
static void ListView_toggle(GtkCellRendererToggle* cell, gchar* path, ListView* self) {
|
2013-11-28 10:29:01 +00:00
|
|
|
unsigned selection = decimal(path);
|
|
|
|
self->setChecked(selection, !self->checked(selection));
|
|
|
|
if(self->onToggle) self->onToggle(selection);
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
2013-05-02 11:25:45 +00:00
|
|
|
void pListView::append(const lstring& text) {
|
2011-02-24 09:27:21 +00:00
|
|
|
GtkTreeIter iter;
|
|
|
|
gtk_list_store_append(store, &iter);
|
2013-11-28 10:29:01 +00:00
|
|
|
for(unsigned n = 0; n < text.size(); n++) {
|
|
|
|
gtk_list_store_set(store, &iter, 1 + n * 2 + 1, (const char*)text[n], -1);
|
|
|
|
}
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
2011-02-27 09:05:10 +00:00
|
|
|
void pListView::autoSizeColumns() {
|
2011-02-24 09:27:21 +00:00
|
|
|
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(subWidget));
|
|
|
|
}
|
|
|
|
|
Update to higan v091r14 and ananke v00r03 releases.
byuu says:
higan changelog:
- generates title displayed in emulator window by asking the core
- core builds title solely from "information/title" ... if it's not
there, you don't get a title at all
- sub-system load menu is gone ... since there are multiple revisions of
the SGB, this never really worked well anyway
- to load an SGB, BS-X or ST cartridge, load the base cartridge first
- "File->Load Game" moved to "Load->Import Game" ... may cause a bit of
confusion to new users, but I don't like having a single-item menu,
we'll just have to explain it to new users
- browser window redone to look like ananke
- home button here goes to ~/Emulation rather than just ~ like ananke,
since this is the home of game folders
- game folder icon is now the executable icon for the Tango theme
(orange diamond), meant to represent a complete game rather than
a game file or archive
ananke changelog:
- outputs GBC games to "Game Boy Color/" instead of "Game Boy/"
- adds the file basename to "information/title"
Known issues:
- using ananke to load a GB game trips the Super Famicom SGB mode and
fails (need to make the full-path auto-detection ignore non-bootable
systems)
- need to dump and test some BS-X media before releasing
- ananke lacks BS-X Satellaview cartridge support
- v092 isn't going to let you retarget the ananke/higan game folder path
of ~/Emulation, you will have to wait for a future version if that
bothers you so greatly
[Later, after the v092 release, byuu posted this additional changelog:
- kill laevateinn
- add title()
- add bootable, remove load
- combine file, library
- combine [][][] paths
- fix SFC subtype handling XML->BML
- update file browser to use buttons
- update file browser keyboard handling
- update system XML->BML
- fix sufami turbo hashing
- remove Cartridge::manifest
]
2012-12-25 05:31:55 +00:00
|
|
|
bool pListView::focused() {
|
|
|
|
return GTK_WIDGET_HAS_FOCUS(subWidget);
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::remove(unsigned selection) {
|
2013-05-02 11:25:45 +00:00
|
|
|
GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
|
2012-06-25 12:49:39 +00:00
|
|
|
GtkTreeIter iter;
|
2013-11-28 10:29:01 +00:00
|
|
|
gtk_tree_model_get_iter_from_string(model, &iter, string(selection));
|
2012-06-25 12:49:39 +00:00
|
|
|
gtk_list_store_remove(store, &iter);
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:27:21 +00:00
|
|
|
void pListView::reset() {
|
2011-02-27 09:05:10 +00:00
|
|
|
listView.state.selected = false;
|
|
|
|
listView.state.selection = 0;
|
2011-02-24 09:27:21 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::setCheckable(bool checkable) {
|
2013-03-15 13:11:33 +00:00
|
|
|
gtk_cell_renderer_set_visible(column(0).checkbutton, checkable);
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setChecked(unsigned selection, bool checked) {
|
2013-05-02 11:25:45 +00:00
|
|
|
GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
|
2011-02-24 09:27:21 +00:00
|
|
|
GtkTreeIter iter;
|
2013-11-28 10:29:01 +00:00
|
|
|
gtk_tree_model_get_iter_from_string(model, &iter, string(selection));
|
2011-02-24 09:27:21 +00:00
|
|
|
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1);
|
|
|
|
}
|
|
|
|
|
2013-05-02 11:25:45 +00:00
|
|
|
void pListView::setHeaderText(const lstring& text) {
|
2011-09-05 03:48:23 +00:00
|
|
|
destructor();
|
|
|
|
constructor();
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::setHeaderVisible(bool visible) {
|
|
|
|
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(subWidget), visible);
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setImage(unsigned selection, unsigned position, const image& image) {
|
2013-05-02 11:25:45 +00:00
|
|
|
GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
|
2012-06-18 10:13:51 +00:00
|
|
|
GtkTreeIter iter;
|
2013-11-28 10:29:01 +00:00
|
|
|
gtk_tree_model_get_iter_from_string(model, &iter, string(selection));
|
2012-06-18 10:13:51 +00:00
|
|
|
if(image.empty() == false) {
|
2013-05-02 11:25:45 +00:00
|
|
|
GdkPixbuf* pixbuf = CreatePixbuf(image, true);
|
2013-11-28 10:29:01 +00:00
|
|
|
gtk_list_store_set(store, &iter, 1 + position * 2, pixbuf, -1);
|
2012-06-18 10:13:51 +00:00
|
|
|
} else {
|
2013-11-28 10:29:01 +00:00
|
|
|
gtk_list_store_set(store, &iter, 1 + position * 2, nullptr, -1);
|
2012-06-18 10:13:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-27 09:05:10 +00:00
|
|
|
void pListView::setSelected(bool selected) {
|
|
|
|
if(selected == false) {
|
2013-05-02 11:25:45 +00:00
|
|
|
GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
|
2011-02-27 09:05:10 +00:00
|
|
|
gtk_tree_selection_unselect_all(selection);
|
|
|
|
} else {
|
|
|
|
setSelection(listView.state.selection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setSelection(unsigned selection) {
|
|
|
|
GtkTreeSelection* treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
|
2013-05-02 11:25:45 +00:00
|
|
|
GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
|
2013-11-28 10:29:01 +00:00
|
|
|
gtk_tree_selection_unselect_all(treeSelection);
|
2011-02-24 09:27:21 +00:00
|
|
|
GtkTreeIter iter;
|
2013-11-28 10:29:01 +00:00
|
|
|
if(gtk_tree_model_get_iter_from_string(model, &iter, string(selection)) == false) return;
|
|
|
|
gtk_tree_selection_select_iter(treeSelection, &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
|
2013-05-02 11:25:45 +00:00
|
|
|
char* path = gtk_tree_model_get_string_from_iter(model, &iter);
|
|
|
|
GtkTreePath* treePath = gtk_tree_path_new_from_string(path);
|
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
|
|
|
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);
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setText(unsigned selection, unsigned position, string text) {
|
|
|
|
GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
|
|
|
|
GtkTreeIter iter;
|
|
|
|
gtk_tree_model_get_iter_from_string(model, &iter, string(selection));
|
|
|
|
gtk_list_store_set(store, &iter, 1 + position * 2 + 1, (const char*)text, -1);
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:27:21 +00:00
|
|
|
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);
|
|
|
|
|
2012-06-18 10:13:51 +00:00
|
|
|
lstring headerText = listView.state.headerText;
|
|
|
|
if(headerText.size() == 0) headerText.append(""); //ListView must have at least one column
|
|
|
|
|
|
|
|
column.reset();
|
|
|
|
vector<GType> gtype;
|
2013-05-02 11:25:45 +00:00
|
|
|
for(auto& text : headerText) {
|
2012-06-18 10:13:51 +00:00
|
|
|
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, "");
|
|
|
|
|
2013-03-15 13:11:33 +00:00
|
|
|
if(column.size() == 0) { //first column checkbutton
|
|
|
|
cell.checkbutton = gtk_cell_renderer_toggle_new();
|
|
|
|
gtk_tree_view_column_pack_start(cell.column, cell.checkbutton, false);
|
|
|
|
gtk_tree_view_column_set_attributes(cell.column, cell.checkbutton, "active", gtype.size(), nullptr);
|
2012-06-18 10:13:51 +00:00
|
|
|
gtype.append(G_TYPE_BOOLEAN);
|
2013-03-15 13:11:33 +00:00
|
|
|
g_signal_connect(cell.checkbutton, "toggled", G_CALLBACK(ListView_toggle), (gpointer)&listView);
|
2012-06-18 10:13:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2011-02-24 09:27:21 +00:00
|
|
|
|
2012-06-18 10:13:51 +00:00
|
|
|
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);
|
2011-02-24 09:27:21 +00:00
|
|
|
|
2012-06-18 10:13:51 +00:00
|
|
|
column.append(cell);
|
|
|
|
}
|
|
|
|
|
|
|
|
store = gtk_list_store_newv(gtype.size(), gtype.data());
|
2011-02-24 09:27:21 +00:00
|
|
|
subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
|
|
|
|
gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
|
|
|
|
g_object_unref(G_OBJECT(store));
|
|
|
|
|
2013-05-02 11:25:45 +00:00
|
|
|
for(auto& cell : column) {
|
2012-06-18 10:13:51 +00:00
|
|
|
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);
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
2013-03-15 13:11:33 +00:00
|
|
|
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(subWidget), headerText.size() >= 2); //two or more columns + checkbutton column
|
2012-06-18 10:13:51 +00:00
|
|
|
gtk_tree_view_set_search_column(GTK_TREE_VIEW(subWidget), 2);
|
2011-02-24 09:27:21 +00:00
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
g_signal_connect(G_OBJECT(subWidget), "cursor-changed", G_CALLBACK(ListView_change), (gpointer)&listView);
|
|
|
|
g_signal_connect(G_OBJECT(subWidget), "row-activated", G_CALLBACK(ListView_activate), (gpointer)&listView);
|
2011-02-24 09:27:21 +00:00
|
|
|
|
2011-09-05 03:48:23 +00:00
|
|
|
gtk_widget_show(subWidget);
|
|
|
|
|
2011-02-24 09:27:21 +00:00
|
|
|
setHeaderVisible(listView.state.headerVisible);
|
|
|
|
setCheckable(listView.state.checkable);
|
2013-05-02 11:25:45 +00:00
|
|
|
for(auto& text : listView.state.text) append(text);
|
2011-09-27 11:55:02 +00:00
|
|
|
for(unsigned n = 0; n < listView.state.checked.size(); n++) setChecked(n, listView.state.checked[n]);
|
2011-02-27 09:05:10 +00:00
|
|
|
if(listView.state.selected) setSelection(listView.state.selection);
|
|
|
|
autoSizeColumns();
|
2011-09-05 03:48:23 +00:00
|
|
|
}
|
2011-02-24 09:27:21 +00:00
|
|
|
|
2011-09-05 03:48:23 +00:00
|
|
|
void pListView::destructor() {
|
|
|
|
gtk_widget_destroy(subWidget);
|
|
|
|
gtk_widget_destroy(gtkWidget);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::orphan() {
|
|
|
|
destructor();
|
|
|
|
constructor();
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::setFocused() {
|
|
|
|
gtk_widget_grab_focus(subWidget);
|
|
|
|
}
|
|
|
|
|
2013-05-05 09:21:30 +00:00
|
|
|
void pListView::setFont(string font) {
|
2011-09-05 03:48:23 +00:00
|
|
|
pFont::setFont(gtkWidget, font);
|
2013-05-02 11:25:45 +00:00
|
|
|
for(auto& cell : column) pFont::setFont(cell.label, font);
|
2011-02-24 09:27:21 +00:00
|
|
|
}
|
2013-03-15 13:11:33 +00:00
|
|
|
|
|
|
|
}
|