From 5286481d8dd447c414e1c6e76bae8f84e70fb4df Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 4 Oct 2010 16:33:49 +1100 Subject: [PATCH] Update to v070r08 release. byuu says: - all three ports of phoenix gain the ability to use ListBox::setCheckable(), checked(row), setChecked(row, checked = true); - cheat editor updated to take advantage of this Some fun differences between the implementations. Windows was the least flexible, it only lets you have a check at the start of each item. Luckily that's all I need for my purposes so it'll work. It's also a lot easier, as now I don't need a ton of extra code to try and set per-column checkboxes. Now both Windows and Qt can put text into the first item with the checkbox, but GTK+ cannot. Further, Qt needs this because even if you hide the checkbox column, it still tries to search for typed text from the checkbox column. GTK+ does this too, but unlike GTK+, Qt lacks an API call to set the search column. Since my code basically has to change this in real-time since you have to call the setProperty functions after create(), this means I always set up the checkbox columns regardless of whether or not they are used. For Qt, I had to work around this and it'll be an annoying edge case if you try and use setCheckable(true) and then setCheckable(false), because Qt has no way to clear the checkboxes from an item once you've enabled them for the first time. But without doing it this way, there's no way for eg the ROM file loader to allow type-searching, so that's the way I do it. Windows works the same, and GTK+ has a separate column (hidden from the phoenix API standpoint) for the checkboxes, with no column header label text. All in all, a major hassle, but it was the only really major GUI hit from leaving Qt, aside from the horror that's going to be the debugger, which needs all kinds of highly specialized controls. --- bsnes/phoenix/gtk/gtk.hpp | 4 + bsnes/phoenix/gtk/listbox.cpp | 82 +++++++++++---- bsnes/phoenix/gtk/object.cpp | 1 + bsnes/phoenix/qt/listbox.cpp | 26 +++++ bsnes/phoenix/qt/qt.hpp | 4 + bsnes/phoenix/qt/qt.moc | 9 +- bsnes/phoenix/qt/qt.moc.hpp | 6 ++ bsnes/phoenix/windows/button.cpp | 2 +- bsnes/phoenix/windows/checkbox.cpp | 2 +- bsnes/phoenix/windows/combobox.cpp | 2 +- bsnes/phoenix/windows/editbox.cpp | 2 +- bsnes/phoenix/windows/label.cpp | 2 +- bsnes/phoenix/windows/listbox.cpp | 18 +++- bsnes/phoenix/windows/menu.cpp | 2 +- bsnes/phoenix/windows/object.cpp | 2 + bsnes/phoenix/windows/radiobox.cpp | 4 +- bsnes/phoenix/windows/textbox.cpp | 2 +- bsnes/phoenix/windows/widget.cpp | 4 +- bsnes/phoenix/windows/windows.cpp | 134 ++++++++++++------------ bsnes/phoenix/windows/windows.hpp | 33 +++--- bsnes/snes/snes.hpp | 2 +- bsnes/ui-phoenix/tools/cheat-editor.cpp | 62 +++++------ bsnes/ui-phoenix/tools/cheat-editor.hpp | 5 +- 23 files changed, 254 insertions(+), 156 deletions(-) diff --git a/bsnes/phoenix/gtk/gtk.hpp b/bsnes/phoenix/gtk/gtk.hpp index 69d3e485..389708bc 100755 --- a/bsnes/phoenix/gtk/gtk.hpp +++ b/bsnes/phoenix/gtk/gtk.hpp @@ -166,14 +166,18 @@ struct Label : Widget { struct ListBox : Widget { nall::function onActivate; nall::function onChange; + nall::function onTick; void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); void setFocused(); void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); void setFont(Font &font); void reset(); void resizeColumnsToContent(); void addItem(const char *text); void setItem(unsigned row, const char *text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); nall::optional selection(); void setSelection(unsigned row); ListBox(); diff --git a/bsnes/phoenix/gtk/listbox.cpp b/bsnes/phoenix/gtk/listbox.cpp index ab04fc7a..dbeab5c3 100755 --- a/bsnes/phoenix/gtk/listbox.cpp +++ b/bsnes/phoenix/gtk/listbox.cpp @@ -1,3 +1,10 @@ +static void ListBox_activate(ListBox *self) { + signed selection = -1; + if(auto position = self->selection()) selection = position(); + self->listBox->selection = selection; + if(self->onActivate) self->onActivate(); +} + static void ListBox_change(ListBox *self) { signed selection = -1; if(auto position = self->selection()) selection = position(); @@ -6,11 +13,10 @@ static void ListBox_change(ListBox *self) { if(self->onChange) self->onChange(); } -static void ListBox_activate(ListBox *self) { - signed selection = -1; - if(auto position = self->selection()) selection = position(); - self->listBox->selection = selection; - if(self->onActivate) self->onActivate(); +static void ListBox_tick(GtkCellRendererToggle *cell, gchar *path_string, ListBox *self) { + unsigned index = strunsigned(path_string); + self->setChecked(index, !self->checked(index)); + if(self->onTick) self->onTick(index); } void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { @@ -22,10 +28,10 @@ void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns gtk_widget_set_size_request(object->widget, width, height); lstring list; - list.split("\t", text); + list.split("\t", string("\t", text)); GType *v = (GType*)malloc(list.size() * sizeof(GType)); - for(unsigned i = 0; i < list.size(); i++) v[i] = G_TYPE_STRING; + for(unsigned i = 0; i < list.size(); i++) v[i] = (i == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING); listBox->store = gtk_list_store_newv(list.size(), v); free(v); @@ -33,20 +39,30 @@ void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); g_object_unref(G_OBJECT(listBox->store)); - //alternate color of each row if there is more than one column - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 2); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false); - for(unsigned i = 0; i < list.size(); i++) { - listBox->column[i].renderer = gtk_cell_renderer_text_new(); - listBox->column[i].column = gtk_tree_view_column_new_with_attributes( - list[i], listBox->column[i].renderer, "text", i, (void*)0 - ); + if(i == 0) { + listBox->column[i].renderer = gtk_cell_renderer_toggle_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + "", listBox->column[i].renderer, "active", i, (void*)0 + ); + gtk_tree_view_column_set_resizable(listBox->column[i].column, false); + gtk_tree_view_column_set_visible(listBox->column[i].column, listBox->checkable); + g_signal_connect(listBox->column[i].renderer, "toggled", G_CALLBACK(ListBox_tick), (gpointer)this); + } else { + listBox->column[i].renderer = gtk_cell_renderer_text_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + "", listBox->column[i].renderer, "text", i, (void*)0 + ); + gtk_tree_view_column_set_resizable(listBox->column[i].column, true); + } listBox->column[i].label = gtk_label_new(list[i]); gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label); gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column); gtk_widget_show(listBox->column[i].label); } + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 3); //>= 2 + one for the checkbox column + gtk_tree_view_set_search_column(GTK_TREE_VIEW(object->subWidget), 1); g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this); g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this); @@ -65,6 +81,11 @@ void ListBox::setHeaderVisible(bool visible) { gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), visible); } +void ListBox::setCheckable(bool checkable) { + listBox->checkable = checkable; + if(object->subWidget) gtk_tree_view_column_set_visible(listBox->column[0].column, checkable); +} + void ListBox::setFont(Font &font) { Widget::setFont(font); unsigned columns = 1; @@ -95,9 +116,8 @@ void ListBox::addItem(const char *text) { list.split("\t", text); GtkTreeIter iter; gtk_list_store_append(listBox->store, &iter); - for(unsigned i = 0; i < list.size(); i++) { - gtk_list_store_set(listBox->store, &iter, i, (const char*)list[i], -1); - } + unsigned index = 1; + foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1); } void ListBox::setItem(unsigned row, const char *text) { @@ -110,9 +130,28 @@ void ListBox::setItem(unsigned row, const char *text) { lstring list; list.split("\t", text); - for(unsigned i = 0; i < list.size(); i++) { - gtk_list_store_set(listBox->store, &iter, i, (const char*)list[i], -1); - } + unsigned index = 1; + foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1); +} + +bool ListBox::checked(unsigned row) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreePath *path = gtk_tree_path_new_from_string(string(row)); + GtkTreeIter iter; + bool state; + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, 0, &state, -1); + gtk_tree_path_free(path); + return state; +} + +void ListBox::setChecked(unsigned row, bool checked) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreePath *path = gtk_tree_path_new_from_string(string(row)); + GtkTreeIter iter; + gtk_tree_model_get_iter(model, &iter, path); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1); + gtk_tree_path_free(path); } optional ListBox::selection() { @@ -152,4 +191,5 @@ void ListBox::setSelection(unsigned row) { ListBox::ListBox() { listBox = new ListBox::Data; + listBox->checkable = false; } diff --git a/bsnes/phoenix/gtk/object.cpp b/bsnes/phoenix/gtk/object.cpp index c17e8f09..f09960ac 100755 --- a/bsnes/phoenix/gtk/object.cpp +++ b/bsnes/phoenix/gtk/object.cpp @@ -43,6 +43,7 @@ struct ListBox::Data { GtkWidget *label; }; linear_vector column; + bool checkable; signed selection; }; diff --git a/bsnes/phoenix/qt/listbox.cpp b/bsnes/phoenix/qt/listbox.cpp index 1cde6ecb..42eb56d6 100755 --- a/bsnes/phoenix/qt/listbox.cpp +++ b/bsnes/phoenix/qt/listbox.cpp @@ -16,6 +16,7 @@ void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns listBox->setAlternatingRowColors(list.size() >= 2); listBox->connect(listBox, SIGNAL(itemActivated(QTreeWidgetItem*, int)), SLOT(onActivate())); listBox->connect(listBox, SIGNAL(itemSelectionChanged()), SLOT(onChange())); + listBox->connect(listBox, SIGNAL(itemChanged(QTreeWidgetItem*, int)), SLOT(onTick(QTreeWidgetItem*))); if(parent.window->defaultFont) listBox->setFont(*parent.window->defaultFont); listBox->show(); } @@ -24,6 +25,14 @@ void ListBox::setHeaderVisible(bool headerVisible) { listBox->setHeaderHidden(headerVisible == false); } +void ListBox::setCheckable(bool checkable) { + listBox->checkable = checkable; + if(listBox->checkable) { + auto items = listBox->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.size(); i++) items[i]->setCheckState(0, Qt::Unchecked); + } +} + void ListBox::reset() { listBox->clear(); } @@ -33,19 +42,36 @@ void ListBox::resizeColumnsToContent() { } void ListBox::addItem(const char *text) { + object->locked = true; auto items = listBox->findItems("", Qt::MatchContains); QTreeWidgetItem *item = new QTreeWidgetItem(listBox); + if(listBox->checkable) item->setCheckState(0, Qt::Unchecked); item->setData(0, Qt::UserRole, (unsigned)items.size()); lstring list; list.split("\t", text); for(unsigned i = 0; i < list.size(); i++) item->setText(i, (const char*)list[i]); + object->locked = false; } void ListBox::setItem(unsigned row, const char *text) { + object->locked = true; QTreeWidgetItem *item = listBox->topLevelItem(row); lstring list; list.split("\t", text); for(unsigned i = 0; i < list.size(); i++) item->setText(i, (const char*)list[i]); + object->locked = false; +} + +bool ListBox::checked(unsigned row) { + QTreeWidgetItem *item = listBox->topLevelItem(row); + return (item ? item->checkState(0) == Qt::Checked : false); +} + +void ListBox::setChecked(unsigned row, bool checked) { + object->locked = true; + QTreeWidgetItem *item = listBox->topLevelItem(row); + if(item) item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked); + object->locked = false; } optional ListBox::selection() { diff --git a/bsnes/phoenix/qt/qt.hpp b/bsnes/phoenix/qt/qt.hpp index d24c81d0..2a571585 100755 --- a/bsnes/phoenix/qt/qt.hpp +++ b/bsnes/phoenix/qt/qt.hpp @@ -220,12 +220,16 @@ struct Label : Widget { struct ListBox : Widget { nall::function onActivate; nall::function onChange; + nall::function onTick; void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); void reset(); void resizeColumnsToContent(); void addItem(const char *text); void setItem(unsigned row, const char *text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); nall::optional selection(); void setSelection(unsigned row); ListBox(); diff --git a/bsnes/phoenix/qt/qt.moc b/bsnes/phoenix/qt/qt.moc index cd12750f..744c9485 100755 --- a/bsnes/phoenix/qt/qt.moc +++ b/bsnes/phoenix/qt/qt.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'qt.moc.hpp' ** -** Created: Sat Oct 2 21:32:41 2010 +** Created: Mon Oct 4 00:53:54 2010 ** by: The Qt Meta Object Compiler version 62 (Qt 4.6.2) ** ** WARNING! All changes made in this file will be lost! @@ -641,7 +641,7 @@ static const uint qt_meta_data_ListBox__Data[] = { 4, // revision 0, // classname 0, 0, // classinfo - 2, 14, // methods + 3, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors @@ -651,12 +651,14 @@ static const uint qt_meta_data_ListBox__Data[] = { // slots: signature, parameters, type, tag, flags 15, 14, 14, 14, 0x0a, 28, 14, 14, 14, 0x0a, + 44, 39, 14, 14, 0x0a, 0 // eod }; static const char qt_meta_stringdata_ListBox__Data[] = { "ListBox::Data\0\0onActivate()\0onChange()\0" + "item\0onTick(QTreeWidgetItem*)\0" }; const QMetaObject ListBox::Data::staticMetaObject = { @@ -690,9 +692,10 @@ int ListBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a) switch (_id) { case 0: onActivate(); break; case 1: onChange(); break; + case 2: onTick((*reinterpret_cast< QTreeWidgetItem*(*)>(_a[1]))); break; default: ; } - _id -= 2; + _id -= 3; } return _id; } diff --git a/bsnes/phoenix/qt/qt.moc.hpp b/bsnes/phoenix/qt/qt.moc.hpp index 4fd5ac81..e57f9baa 100755 --- a/bsnes/phoenix/qt/qt.moc.hpp +++ b/bsnes/phoenix/qt/qt.moc.hpp @@ -213,8 +213,10 @@ struct ListBox::Data : public QTreeWidget { public: ListBox &self; + bool checkable; Data(ListBox &self) : self(self) { + checkable = false; } public slots: @@ -225,6 +227,10 @@ public slots: void onChange() { if(self.object->locked == false && self.onChange) self.onChange(); } + + void onTick(QTreeWidgetItem *item) { + if(self.object->locked == false && self.onTick) self.onTick(item->data(0, Qt::UserRole).toUInt()); + } }; struct ProgressBar::Data : public QProgressBar { diff --git a/bsnes/phoenix/windows/button.cpp b/bsnes/phoenix/windows/button.cpp index edd88705..06b04efd 100755 --- a/bsnes/phoenix/windows/button.cpp +++ b/bsnes/phoenix/windows/button.cpp @@ -6,5 +6,5 @@ void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsi parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); } diff --git a/bsnes/phoenix/windows/checkbox.cpp b/bsnes/phoenix/windows/checkbox.cpp index e1ca0e5c..39164391 100755 --- a/bsnes/phoenix/windows/checkbox.cpp +++ b/bsnes/phoenix/windows/checkbox.cpp @@ -6,7 +6,7 @@ void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, un parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); } bool CheckBox::checked() { diff --git a/bsnes/phoenix/windows/combobox.cpp b/bsnes/phoenix/windows/combobox.cpp index 99e8cccf..6cba64ac 100755 --- a/bsnes/phoenix/windows/combobox.cpp +++ b/bsnes/phoenix/windows/combobox.cpp @@ -7,7 +7,7 @@ void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, un ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); //CreateWindow height parameter is the height of the expanded list box; //need additional code to override default ComboBox control height diff --git a/bsnes/phoenix/windows/editbox.cpp b/bsnes/phoenix/windows/editbox.cpp index 21a2e01d..f9091f1e 100755 --- a/bsnes/phoenix/windows/editbox.cpp +++ b/bsnes/phoenix/windows/editbox.cpp @@ -8,7 +8,7 @@ void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns ); setText(text); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); } string EditBox::getText() { diff --git a/bsnes/phoenix/windows/label.cpp b/bsnes/phoenix/windows/label.cpp index eb047d80..12c54183 100755 --- a/bsnes/phoenix/windows/label.cpp +++ b/bsnes/phoenix/windows/label.cpp @@ -6,7 +6,7 @@ void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsig parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); setText(text); } diff --git a/bsnes/phoenix/windows/listbox.cpp b/bsnes/phoenix/windows/listbox.cpp index 51b9d1bf..f2bf4ba1 100755 --- a/bsnes/phoenix/windows/listbox.cpp +++ b/bsnes/phoenix/windows/listbox.cpp @@ -7,7 +7,7 @@ void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); ListView_SetExtendedListViewStyle(widget->window, LVS_EX_FULLROWSELECT); lstring list; @@ -34,6 +34,10 @@ void ListBox::setHeaderVisible(bool headerVisible) { ); } +void ListBox::setCheckable(bool checkable) { + ListView_SetExtendedListViewStyle(widget->window, LVS_EX_FULLROWSELECT | (checkable ? LVS_EX_CHECKBOXES : 0)); +} + void ListBox::reset() { ListView_DeleteAllItems(widget->window); } @@ -54,7 +58,9 @@ void ListBox::addItem(const char *text) { item.iSubItem = 0; utf16_t wtext(list[0]); item.pszText = wtext; + object->locked = true; ListView_InsertItem(widget->window, &item); + object->locked = false; for(unsigned i = 1; i < list.size(); i++) { utf16_t wtext(list[i]); ListView_SetItemText(widget->window, row, i, wtext); @@ -86,6 +92,16 @@ void ListBox::setSelection(unsigned row) { } } +bool ListBox::checked(unsigned row) { + return ListView_GetCheckState(widget->window, row); +} + +void ListBox::setChecked(unsigned row, bool checked) { + object->locked = true; + ListView_SetCheckState(widget->window, row, checked); + object->locked = false; +} + ListBox::ListBox() { listBox = new ListBox::Data; listBox->lostFocus = false; diff --git a/bsnes/phoenix/windows/menu.cpp b/bsnes/phoenix/windows/menu.cpp index ed24a15a..a7a36b03 100755 --- a/bsnes/phoenix/windows/menu.cpp +++ b/bsnes/phoenix/windows/menu.cpp @@ -1,5 +1,5 @@ Action::Action() { - os.objects.append(this); + OS::os->objects.append(this); action = new Action::Data; } diff --git a/bsnes/phoenix/windows/object.cpp b/bsnes/phoenix/windows/object.cpp index d9e7002d..2a5b8785 100755 --- a/bsnes/phoenix/windows/object.cpp +++ b/bsnes/phoenix/windows/object.cpp @@ -70,6 +70,7 @@ struct VerticalSlider::Data { }; struct OS::Data { + nall::array objects; HFONT proportionalFont; HFONT monospaceFont; }; @@ -78,6 +79,7 @@ void Object::unused() { } Object::Object() { + OS::initialize(); static unsigned guid = 100; object = new Object::Data; object->id = guid++; diff --git a/bsnes/phoenix/windows/radiobox.cpp b/bsnes/phoenix/windows/radiobox.cpp index f86e943d..27bb692b 100755 --- a/bsnes/phoenix/windows/radiobox.cpp +++ b/bsnes/phoenix/windows/radiobox.cpp @@ -9,7 +9,7 @@ void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, un parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); setChecked(); } @@ -24,7 +24,7 @@ void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, GetParent(radioBox->parent->widget->window), (HMENU)object->id, GetModuleHandle(0), 0 ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(radioBox->parentWindow->window->defaultFont ? radioBox->parentWindow->window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(radioBox->parentWindow->window->defaultFont ? radioBox->parentWindow->window->defaultFont : OS::os->proportionalFont), 0); } bool RadioBox::checked() { diff --git a/bsnes/phoenix/windows/textbox.cpp b/bsnes/phoenix/windows/textbox.cpp index d815dab9..ea3b2670 100755 --- a/bsnes/phoenix/windows/textbox.cpp +++ b/bsnes/phoenix/windows/textbox.cpp @@ -6,7 +6,7 @@ void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); - SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); } string TextBox::text() { diff --git a/bsnes/phoenix/windows/widget.cpp b/bsnes/phoenix/windows/widget.cpp index 1741a9b5..70ac5301 100755 --- a/bsnes/phoenix/windows/widget.cpp +++ b/bsnes/phoenix/windows/widget.cpp @@ -33,8 +33,8 @@ void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height } Widget::Widget() { - os.objects.append(this); + OS::os->objects.append(this); widget = new Widget::Data; widget->window = 0; - widget->font = os.os->proportionalFont; + widget->font = OS::os->proportionalFont; } diff --git a/bsnes/phoenix/windows/windows.cpp b/bsnes/phoenix/windows/windows.cpp index 8a1ce782..14bd25c5 100755 --- a/bsnes/phoenix/windows/windows.cpp +++ b/bsnes/phoenix/windows/windows.cpp @@ -29,14 +29,72 @@ namespace phoenix { #include "viewport.cpp" #include "messagewindow.cpp" -OS &os = OS::handle(); +OS::Data *OS::os = 0; Window Window::None; static void OS_keyboardProc(HWND, UINT, WPARAM, LPARAM); +static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); -OS& OS::handle() { - static OS os; - return os; +void OS::initialize() { + static bool initialized = false; + if(initialized == true) return; + initialized = true; + + InitCommonControls(); + CoInitialize(0); + + os = new OS::Data; + os->proportionalFont = Font_createFont("Tahoma", 8, false, false); + os->monospaceFont = Font_createFont("Courier New", 8, false, false); + + WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(2)); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = OS_windowProc; + wc.lpszClassName = L"phoenix_window"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Canvas_windowProc; + wc.lpszClassName = L"phoenix_canvas"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Label_windowProc; + wc.lpszClassName = L"phoenix_label"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = Viewport_windowProc; + wc.lpszClassName = L"phoenix_viewport"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); } bool OS::pending() { @@ -268,7 +326,7 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM unsigned id = LOWORD(wparam); HWND control = GetDlgItem(window.widget->window, id); if(control == 0) { - Object *object_ptr = (Object*)os.findObject(id); + Object *object_ptr = (Object*)OS::findObject(id); if(object_ptr) { if(dynamic_cast(object_ptr)) { MenuItem &menuItem = (MenuItem&)*object_ptr; @@ -334,8 +392,12 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM ListBox &listBox = (ListBox&)*object_ptr; LPNMHDR nmhdr = (LPNMHDR)lparam; LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam; + if(nmhdr->code == LVN_ITEMCHANGED && (nmlistview->uChanged & LVIF_STATE)) { - if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) { + unsigned imagemask = ((nmlistview->uNewState & LVIS_STATEIMAGEMASK) >> 12) - 1; + if(imagemask == 0 || imagemask == 1) { + if(listBox.object->locked == false && listBox.onTick) listBox.onTick(nmlistview->iItem); + } else if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) { listBox.listBox->lostFocus = true; } else { if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) { @@ -392,66 +454,8 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM } Object* OS::findObject(unsigned id) { - foreach(object, objects) { if(object->object->id == id) return object; } + foreach(object, os->objects) { if(object->object->id == id) return object; } return 0; } -OS::OS() { - InitCommonControls(); - CoInitialize(0); - - os = new OS::Data; - os->proportionalFont = Font_createFont("Tahoma", 8, false, false); - os->monospaceFont = Font_createFont("Courier New", 8, false, false); - - WNDCLASS wc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); - wc.hCursor = LoadCursor(0, IDC_ARROW); - wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(2)); - wc.hInstance = GetModuleHandle(0); - wc.lpfnWndProc = OS_windowProc; - wc.lpszClassName = L"phoenix_window"; - wc.lpszMenuName = 0; - wc.style = CS_HREDRAW | CS_VREDRAW; - RegisterClass(&wc); - - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); - wc.hCursor = LoadCursor(0, IDC_ARROW); - wc.hIcon = LoadIcon(0, IDI_APPLICATION); - wc.hInstance = GetModuleHandle(0); - wc.lpfnWndProc = Canvas_windowProc; - wc.lpszClassName = L"phoenix_canvas"; - wc.lpszMenuName = 0; - wc.style = CS_HREDRAW | CS_VREDRAW; - RegisterClass(&wc); - - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); - wc.hCursor = LoadCursor(0, IDC_ARROW); - wc.hIcon = LoadIcon(0, IDI_APPLICATION); - wc.hInstance = GetModuleHandle(0); - wc.lpfnWndProc = Label_windowProc; - wc.lpszClassName = L"phoenix_label"; - wc.lpszMenuName = 0; - wc.style = CS_HREDRAW | CS_VREDRAW; - RegisterClass(&wc); - - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); - wc.hCursor = LoadCursor(0, IDC_ARROW); - wc.hIcon = LoadIcon(0, IDI_APPLICATION); - wc.hInstance = GetModuleHandle(0); - wc.lpfnWndProc = Viewport_windowProc; - wc.lpszClassName = L"phoenix_viewport"; - wc.lpszMenuName = 0; - wc.style = CS_HREDRAW | CS_VREDRAW; - RegisterClass(&wc); -} - } diff --git a/bsnes/phoenix/windows/windows.hpp b/bsnes/phoenix/windows/windows.hpp index 5e0044e3..763e9750 100755 --- a/bsnes/phoenix/windows/windows.hpp +++ b/bsnes/phoenix/windows/windows.hpp @@ -178,12 +178,16 @@ struct Label : Widget { struct ListBox : Widget { nall::function onActivate; nall::function onChange; + nall::function onTick; void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); void setHeaderVisible(bool headerVisible = true); + void setCheckable(bool checkable = true); void reset(); void resizeColumnsToContent(); void addItem(const char *text); void setItem(unsigned row, const char *text); + bool checked(unsigned row); + void setChecked(unsigned row, bool checked = true); nall::optional selection(); void setSelection(unsigned row); ListBox(); @@ -254,26 +258,21 @@ struct MessageWindow : Object { }; struct OS : Object { - bool pending(); - void run(); - void main(); - void quit(); - unsigned desktopWidth(); - unsigned desktopHeight(); - nall::string folderSelect(Window &parent, const char *path = ""); - nall::string fileOpen(Window &parent, const char *filter, const char *path = ""); - nall::string fileSave(Window &parent, const char *filter, const char *path = ""); + static bool pending(); + static void run(); + static void main(); + static void quit(); + static unsigned desktopWidth(); + static unsigned desktopHeight(); + static nall::string folderSelect(Window &parent, const char *path = ""); + static nall::string fileOpen(Window &parent, const char *filter, const char *path = ""); + static nall::string fileSave(Window &parent, const char *filter, const char *path = ""); //private: - static OS& handle(); + static void initialize(); struct Data; - Data *os; - Object* findObject(unsigned id); - nall::array objects; -private: - OS(); + static Data *os; + static Object* findObject(unsigned id); friend class Object; }; -extern OS &os; - }; diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 10b6122e..844c2b49 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,7 +1,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "070.07"; + static const char Version[] = "070.08"; static const unsigned SerializerVersion = 13; } } diff --git a/bsnes/ui-phoenix/tools/cheat-editor.cpp b/bsnes/ui-phoenix/tools/cheat-editor.cpp index 065cc08d..d358b0a9 100755 --- a/bsnes/ui-phoenix/tools/cheat-editor.cpp +++ b/bsnes/ui-phoenix/tools/cheat-editor.cpp @@ -5,10 +5,9 @@ void CheatEditor::load(string filename) { cheatList.reset(); for(unsigned i = 0; i < 128; i++) { cheatList.addItem(""); - cheatText[i][0] = strunsigned<3, ' '>(i + 1); - cheatText[i][1] = " "; - cheatText[i][2] = ""; - cheatText[i][3] = ""; + cheatText[i][CheatSlot] = strunsigned<3, ' '>(i + 1); + cheatText[i][CheatCode] = ""; + cheatText[i][CheatDesc] = ""; } unsigned n = 0; @@ -32,9 +31,8 @@ void CheatEditor::load(string filename) { code.rtrim("+"); SNES::cheat[n].enabled = enabled; SNES::cheat[n] = code; - cheatText[n][1] = (enabled == false ? " " : "*"); - cheatText[n][2] = code; - cheatText[n][3] = description; + cheatText[n][CheatCode] = code; + cheatText[n][CheatDesc] = description; if(++n >= 128) break; } } @@ -48,7 +46,7 @@ void CheatEditor::load(string filename) { void CheatEditor::save(string filename) { signed lastSave = -1; for(signed i = 127; i >= 0; i--) { - if(cheatText[i][2] != "" || cheatText[i][3] != "") { + if(cheatText[i][CheatCode] != "" || cheatText[i][CheatDesc] != "") { lastSave = i; break; } @@ -63,10 +61,10 @@ void CheatEditor::save(string filename) { fp.print("\n"); fp.print(string("\n")); for(unsigned i = 0; i <= lastSave; i++) { - fp.print(string(" \n")); - fp.print(string(" \n")); + fp.print(string(" \n")); + fp.print(string(" \n")); lstring list; - list.split("+", cheatText[i][2]); + list.split("+", cheatText[i][CheatCode]); foreach(code, list) { fp.print(string(" ", code, "\n")); } @@ -89,6 +87,7 @@ void CheatEditor::create() { cheatList.create(*this, x, y, 500, 250, "Slot\tCode\tDescription"); y += 255; cheatList.setHeaderVisible(); + cheatList.setCheckable(); codeLabel.create(*this, x, y, 80, Style::TextBoxHeight, "Code(s):"); codeEdit.create (*this, x + 80, y, 420, Style::TextBoxHeight); y += Style::TextBoxHeight + 5; @@ -102,8 +101,8 @@ void CheatEditor::create() { setGeometry(160, 160, 510, y); synchronize(); - cheatList.onActivate = { &CheatEditor::toggle, this }; cheatList.onChange = { &CheatEditor::synchronize, this }; + cheatList.onTick = { &CheatEditor::toggle, this }; codeEdit.onChange = descEdit.onChange = { &CheatEditor::bind, this }; clearAllButton.onTick = { &CheatEditor::clearAll, this }; clearButton.onTick = { &CheatEditor::clear, this }; @@ -112,8 +111,8 @@ void CheatEditor::create() { void CheatEditor::synchronize() { clearAllButton.setEnabled(SNES::cartridge.loaded()); if(auto position = cheatList.selection()) { - codeEdit.setText(cheatText[position()][2]); - descEdit.setText(cheatText[position()][3]); + codeEdit.setText(cheatText[position()][1]); + descEdit.setText(cheatText[position()][2]); codeEdit.setEnabled(true); descEdit.setEnabled(true); clearButton.setEnabled(true); @@ -130,35 +129,28 @@ void CheatEditor::refresh() { SNES::cheat.synchronize(); for(unsigned i = 0; i < 128; i++) { lstring list; - list.split("+", cheatText[i][2]); + list.split("+", cheatText[i][CheatCode]); string cheatCode = list[0]; if(list.size() > 1) cheatCode.append("..."); + cheatList.setChecked(i, SNES::cheat[i].enabled); cheatList.setItem(i, string( - cheatText[i][0], cheatText[i][1], "\t", cheatCode, "\t", cheatText[i][3] + cheatText[i][CheatSlot], "\t", cheatCode, "\t", cheatText[i][CheatDesc] )); } cheatList.resizeColumnsToContent(); } -void CheatEditor::toggle() { - if(auto position = cheatList.selection()) { - if(cheatText[position()][1] == " ") { - cheatText[position()][1] = "*"; - SNES::cheat[position()].enabled = true; - } else { - cheatText[position()][1] = " "; - SNES::cheat[position()].enabled = false; - } - } +void CheatEditor::toggle(unsigned row) { + SNES::cheat[row].enabled = cheatList.checked(row); refresh(); } void CheatEditor::bind() { if(auto position = cheatList.selection()) { - cheatText[position()][2] = codeEdit.text(); - cheatText[position()][3] = descEdit.text(); - SNES::cheat[position()] = cheatText[position()][2]; + cheatText[position()][CheatCode] = codeEdit.text(); + cheatText[position()][CheatDesc] = descEdit.text(); + SNES::cheat[position()] = cheatText[position()][CheatCode]; refresh(); } } @@ -168,9 +160,9 @@ void CheatEditor::clearAll() { for(unsigned i = 0; i < 128; i++) { SNES::cheat[i].enabled = false; SNES::cheat[i] = ""; - cheatText[i][1] = " "; - cheatText[i][2] = ""; - cheatText[i][3] = ""; + cheatList.setChecked(i, false); + cheatText[i][CheatCode] = ""; + cheatText[i][CheatDesc] = ""; } SNES::cheat.synchronize(); refresh(); @@ -183,9 +175,9 @@ void CheatEditor::clear() { if(auto position = cheatList.selection()) { SNES::cheat[position()].enabled = false; SNES::cheat[position()] = ""; - cheatText[position()][1] = " "; - cheatText[position()][2] = ""; - cheatText[position()][3] = ""; + cheatList.setChecked(position(), false); + cheatText[position()][CheatCode] = ""; + cheatText[position()][CheatDesc] = ""; SNES::cheat.synchronize(); refresh(); codeEdit.setText(""); diff --git a/bsnes/ui-phoenix/tools/cheat-editor.hpp b/bsnes/ui-phoenix/tools/cheat-editor.hpp index e588b8fe..5ecbea2d 100755 --- a/bsnes/ui-phoenix/tools/cheat-editor.hpp +++ b/bsnes/ui-phoenix/tools/cheat-editor.hpp @@ -12,10 +12,11 @@ struct CheatEditor : Window { void create(); private: - string cheatText[128][4]; + enum : unsigned { CheatSlot, CheatCode, CheatDesc }; + string cheatText[128][3]; void synchronize(); void refresh(); - void toggle(); + void toggle(unsigned row); void bind(); void clearAll(); void clear();