From c31543ea5842b40d0dbe4419ea728cb866836b9a Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Thu, 24 Feb 2011 20:27:21 +1100 Subject: [PATCH] Update to v075r15 release. byuu says: phoenix/GTK+ rewrite completed. All three targets should now be 100% operational with full resize support. --- bsnes/phoenix/core/core.cpp | 2 + bsnes/phoenix/gtk/action/action.cpp | 22 ++ bsnes/phoenix/gtk/action/menu-check-item.cpp | 22 ++ bsnes/phoenix/gtk/action/menu-item.cpp | 12 + bsnes/phoenix/gtk/action/menu-radio-item.cpp | 33 ++ bsnes/phoenix/gtk/action/menu-separator.cpp | 3 + bsnes/phoenix/gtk/action/menu.cpp | 19 + bsnes/phoenix/gtk/font.cpp | 22 ++ bsnes/phoenix/gtk/gtk.cpp | 145 ++++++++ bsnes/phoenix/gtk/gtk.hpp | 348 ++++++++++++++++++ bsnes/phoenix/gtk/message-window.cpp | 61 +++ bsnes/phoenix/gtk/settings.cpp | 22 ++ bsnes/phoenix/gtk/widget/button.cpp | 12 + bsnes/phoenix/gtk/widget/check-box.cpp | 22 ++ bsnes/phoenix/gtk/widget/combo-box.cpp | 33 ++ bsnes/phoenix/gtk/widget/hex-edit.cpp | 246 +++++++++++++ .../phoenix/gtk/widget/horizontal-slider.cpp | 24 ++ bsnes/phoenix/gtk/widget/label.cpp | 8 + bsnes/phoenix/gtk/widget/line-edit.cpp | 27 ++ bsnes/phoenix/gtk/widget/list-view.cpp | 195 ++++++++++ bsnes/phoenix/gtk/widget/progress-bar.cpp | 8 + bsnes/phoenix/gtk/widget/radio-box.cpp | 32 ++ bsnes/phoenix/gtk/widget/text-edit.cpp | 48 +++ bsnes/phoenix/gtk/widget/vertical-slider.cpp | 24 ++ bsnes/phoenix/gtk/widget/viewport.cpp | 15 + bsnes/phoenix/gtk/widget/widget.cpp | 40 ++ bsnes/phoenix/gtk/window.cpp | 232 ++++++++++++ bsnes/phoenix/phoenix.cpp | 11 + bsnes/phoenix/qt/qt.moc | 2 +- bsnes/phoenix/windows/widget/combo-box.cpp | 8 + bsnes/phoenix/windows/widget/list-view.cpp | 5 + bsnes/phoenix/windows/widget/widget.cpp | 11 +- bsnes/phoenix/windows/window.cpp | 16 +- bsnes/phoenix/windows/windows.hpp | 4 +- bsnes/snes/snes.hpp | 4 +- 35 files changed, 1731 insertions(+), 7 deletions(-) create mode 100755 bsnes/phoenix/gtk/action/action.cpp create mode 100755 bsnes/phoenix/gtk/action/menu-check-item.cpp create mode 100755 bsnes/phoenix/gtk/action/menu-item.cpp create mode 100755 bsnes/phoenix/gtk/action/menu-radio-item.cpp create mode 100755 bsnes/phoenix/gtk/action/menu-separator.cpp create mode 100755 bsnes/phoenix/gtk/action/menu.cpp create mode 100755 bsnes/phoenix/gtk/font.cpp create mode 100755 bsnes/phoenix/gtk/gtk.cpp create mode 100755 bsnes/phoenix/gtk/gtk.hpp create mode 100755 bsnes/phoenix/gtk/message-window.cpp create mode 100755 bsnes/phoenix/gtk/settings.cpp create mode 100755 bsnes/phoenix/gtk/widget/button.cpp create mode 100755 bsnes/phoenix/gtk/widget/check-box.cpp create mode 100755 bsnes/phoenix/gtk/widget/combo-box.cpp create mode 100755 bsnes/phoenix/gtk/widget/hex-edit.cpp create mode 100755 bsnes/phoenix/gtk/widget/horizontal-slider.cpp create mode 100755 bsnes/phoenix/gtk/widget/label.cpp create mode 100755 bsnes/phoenix/gtk/widget/line-edit.cpp create mode 100755 bsnes/phoenix/gtk/widget/list-view.cpp create mode 100755 bsnes/phoenix/gtk/widget/progress-bar.cpp create mode 100755 bsnes/phoenix/gtk/widget/radio-box.cpp create mode 100755 bsnes/phoenix/gtk/widget/text-edit.cpp create mode 100755 bsnes/phoenix/gtk/widget/vertical-slider.cpp create mode 100755 bsnes/phoenix/gtk/widget/viewport.cpp create mode 100755 bsnes/phoenix/gtk/widget/widget.cpp create mode 100755 bsnes/phoenix/gtk/window.cpp diff --git a/bsnes/phoenix/core/core.cpp b/bsnes/phoenix/core/core.cpp index 88c84a28..8e7a4108 100755 --- a/bsnes/phoenix/core/core.cpp +++ b/bsnes/phoenix/core/core.cpp @@ -7,6 +7,8 @@ #include "../windows/windows.cpp" #elif defined(PHOENIX_QT) #include "../qt/qt.cpp" +#elif defined(PHOENIX_GTK) + #include "../gtk/gtk.cpp" #elif defined(PHOENIX_REFERENCE) #include "../reference/reference.cpp" #endif diff --git a/bsnes/phoenix/gtk/action/action.cpp b/bsnes/phoenix/gtk/action/action.cpp new file mode 100755 index 00000000..4fd12f0a --- /dev/null +++ b/bsnes/phoenix/gtk/action/action.cpp @@ -0,0 +1,22 @@ +static void Action_setFont(GtkWidget *widget, gpointer font) { + if(font == 0) return; + gtk_widget_modify_font(widget, (PangoFontDescription*)font); + if(GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, (PangoFontDescription*)font); + } +} + +void pAction::setEnabled(bool enabled) { + gtk_widget_set_sensitive(widget, enabled); +} + +void pAction::setFont(Font &font) { + Action_setFont(widget, font.p.gtkFont); +} + +void pAction::setVisible(bool visible) { + gtk_widget_set_visible(widget, visible); +} + +void pAction::constructor() { +} diff --git a/bsnes/phoenix/gtk/action/menu-check-item.cpp b/bsnes/phoenix/gtk/action/menu-check-item.cpp new file mode 100755 index 00000000..dc5c35c2 --- /dev/null +++ b/bsnes/phoenix/gtk/action/menu-check-item.cpp @@ -0,0 +1,22 @@ +static void MenuCheckItem_tick(MenuCheckItem *self) { + if(self->p.locked == false && self->onTick) self->onTick(); +} + +bool pMenuCheckItem::checked() { + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); +} + +void pMenuCheckItem::setChecked(bool checked) { + locked = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), checked); + locked = false; +} + +void pMenuCheckItem::setText(const string &text) { + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text); +} + +void pMenuCheckItem::constructor() { + widget = gtk_check_menu_item_new_with_label(""); + g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(MenuCheckItem_tick), (gpointer)&menuCheckItem); +} diff --git a/bsnes/phoenix/gtk/action/menu-item.cpp b/bsnes/phoenix/gtk/action/menu-item.cpp new file mode 100755 index 00000000..b1adaf33 --- /dev/null +++ b/bsnes/phoenix/gtk/action/menu-item.cpp @@ -0,0 +1,12 @@ +static void MenuItem_tick(MenuItem *self) { + if(self->onTick) self->onTick(); +} + +void pMenuItem::setText(const string &text) { + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text); +} + +void pMenuItem::constructor() { + widget = gtk_menu_item_new_with_label(""); + g_signal_connect_swapped(G_OBJECT(widget), "activate", G_CALLBACK(MenuItem_tick), (gpointer)&menuItem); +} diff --git a/bsnes/phoenix/gtk/action/menu-radio-item.cpp b/bsnes/phoenix/gtk/action/menu-radio-item.cpp new file mode 100755 index 00000000..6ba91d55 --- /dev/null +++ b/bsnes/phoenix/gtk/action/menu-radio-item.cpp @@ -0,0 +1,33 @@ +static void MenuRadioItem_tick(MenuRadioItem *self) { + if(self->p.locked == false && self->checked() && self->onTick) self->onTick(); +} + +bool pMenuRadioItem::checked() { + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); +} + +void pMenuRadioItem::setChecked() { + locked = true; + foreach(item, menuRadioItem.state.group) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item.p.widget), false); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), true); + locked = false; +} + +void pMenuRadioItem::setGroup(const reference_array &group) { + foreach(item, group, n) { + if(n == 0) continue; + GSList *currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(group[0].p.widget)); + if(currentGroup != gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item.p.widget))) { + gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item.p.widget), currentGroup); + } + } +} + +void pMenuRadioItem::setText(const string &text) { + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text); +} + +void pMenuRadioItem::constructor() { + widget = gtk_radio_menu_item_new_with_label(0, ""); + g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)&menuRadioItem); +} diff --git a/bsnes/phoenix/gtk/action/menu-separator.cpp b/bsnes/phoenix/gtk/action/menu-separator.cpp new file mode 100755 index 00000000..aef9a727 --- /dev/null +++ b/bsnes/phoenix/gtk/action/menu-separator.cpp @@ -0,0 +1,3 @@ +void pMenuSeparator::constructor() { + widget = gtk_separator_menu_item_new(); +} diff --git a/bsnes/phoenix/gtk/action/menu.cpp b/bsnes/phoenix/gtk/action/menu.cpp new file mode 100755 index 00000000..27e0936c --- /dev/null +++ b/bsnes/phoenix/gtk/action/menu.cpp @@ -0,0 +1,19 @@ +void pMenu::append(Action &action) { + gtk_menu_shell_append(GTK_MENU_SHELL(submenu), action.p.widget); + gtk_widget_show(action.p.widget); +} + +void pMenu::setFont(Font &font) { + pAction::setFont(font); + foreach(item, menu.state.action) item.p.setFont(font); +} + +void pMenu::setText(const string &text) { + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text); +} + +void pMenu::constructor() { + submenu = gtk_menu_new(); + widget = gtk_menu_item_new_with_label(""); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), submenu); +} diff --git a/bsnes/phoenix/gtk/font.cpp b/bsnes/phoenix/gtk/font.cpp new file mode 100755 index 00000000..7ece6ad8 --- /dev/null +++ b/bsnes/phoenix/gtk/font.cpp @@ -0,0 +1,22 @@ +void pFont::setBold(bool bold) { + pango_font_description_set_weight(gtkFont, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); +} + +void pFont::setFamily(const string &family) { + pango_font_description_set_family(gtkFont, family); +} + +void pFont::setItalic(bool italic) { + pango_font_description_set_style(gtkFont, italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL); +} + +void pFont::setSize(unsigned size) { + pango_font_description_set_size(gtkFont, size * PANGO_SCALE); +} + +void pFont::setUnderline(bool underline) { +} + +void pFont::constructor() { + gtkFont = pango_font_description_new(); +} diff --git a/bsnes/phoenix/gtk/gtk.cpp b/bsnes/phoenix/gtk/gtk.cpp new file mode 100755 index 00000000..37014a83 --- /dev/null +++ b/bsnes/phoenix/gtk/gtk.cpp @@ -0,0 +1,145 @@ +#include "gtk.hpp" + +#include "settings.cpp" +#include "font.cpp" +#include "message-window.cpp" +#include "window.cpp" + +#include "action/action.cpp" +#include "action/menu.cpp" +#include "action/menu-separator.cpp" +#include "action/menu-item.cpp" +#include "action/menu-check-item.cpp" +#include "action/menu-radio-item.cpp" + +#include "widget/widget.cpp" +#include "widget/button.cpp" +#include "widget/check-box.cpp" +#include "widget/combo-box.cpp" +#include "widget/hex-edit.cpp" +#include "widget/horizontal-slider.cpp" +#include "widget/label.cpp" +#include "widget/line-edit.cpp" +#include "widget/list-view.cpp" +#include "widget/progress-bar.cpp" +#include "widget/radio-box.cpp" +#include "widget/text-edit.cpp" +#include "widget/vertical-slider.cpp" +#include "widget/viewport.cpp" + +unsigned pOS::desktopWidth() { + return gdk_screen_get_width(gdk_screen_get_default()); +} + +unsigned pOS::desktopHeight() { + return gdk_screen_get_height(gdk_screen_get_default()); +} + +static string pOS_fileDialog(bool save, Window &parent, const string &path, const lstring &filter) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + save == 0 ? "Load File" : "Save File", + &parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0, + save == 0 ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + foreach(filterItem, filter) { + GtkFileFilter *gtkFilter = gtk_file_filter_new(); + gtk_file_filter_set_name(gtkFilter, filterItem); + lstring part; + part.split("(", filterItem); + part[1].rtrim<1>(")"); + lstring list; + list.split(",", part[1]); + foreach(pattern, list) gtk_file_filter_add_pattern(gtkFilter, pattern); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), gtkFilter); + } + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + return name; +} + +string pOS::fileLoad(Window &parent, const string &path, const lstring &filter) { + return pOS_fileDialog(0, parent, path, filter); +} + +string pOS::fileSave(Window &parent, const string &path, const lstring &filter) { + return pOS_fileDialog(1, parent, path, filter); +} + +string pOS::folderSelect(Window &parent, const string &path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Select Folder", + &parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + if(name == "") return ""; + if(name.endswith("/") == false) name.append("/"); + return name; +} + +void pOS::main() { + gtk_main(); +} + +bool pOS::pending() { + return gtk_events_pending(); +} + +void pOS::process() { + while(pending()) gtk_main_iteration_do(false); +} + +void pOS::quit() { + settings.save(); + gtk_main_quit(); +} + +void pOS::initialize() { + settings.load(); + + int argc = 1; + char *argv[2]; + argv[0] = new char[8]; + argv[1] = 0; + strcpy(argv[0], "phoenix"); + char **argvp = argv; + gtk_init(&argc, &argvp); + + gtk_rc_parse_string( + "style \"phoenix-gtk\"\n" + "{\n" + " GtkComboBox::appears-as-list = 1\n" + " GtkTreeView::vertical-separator = 0\n" + "}\n" + "class \"GtkComboBox\" style \"phoenix-gtk\"\n" + "class \"GtkTreeView\" style \"phoenix-gtk\"\n" + ); +} diff --git a/bsnes/phoenix/gtk/gtk.hpp b/bsnes/phoenix/gtk/gtk.hpp new file mode 100755 index 00000000..a19d3889 --- /dev/null +++ b/bsnes/phoenix/gtk/gtk.hpp @@ -0,0 +1,348 @@ +struct Settings : public configuration { + unsigned frameGeometryX; + unsigned frameGeometryY; + unsigned frameGeometryWidth; + unsigned frameGeometryHeight; + + void load(); + void save(); + Settings(); +}; + +struct pFont; +struct pWindow; +struct pMenu; +struct pLayout; +struct pWidget; + +struct pObject { + bool locked; + + pObject() { + locked = false; + } +}; + +struct pOS : public pObject { + static unsigned desktopWidth(); + static unsigned desktopHeight(); + static string fileLoad(Window &parent, const string &path, const lstring &filter); + static string fileSave(Window &parent, const string &path, const lstring &filter); + static string folderSelect(Window &parent, const string &path); + static void main(); + static bool pending(); + static void process(); + static void quit(); + + static void initialize(); +}; + +struct pFont : public pObject { + Font &font; + PangoFontDescription *gtkFont; + + void setBold(bool bold); + void setFamily(const string &family); + void setItalic(bool italic); + void setSize(unsigned size); + void setUnderline(bool underline); + + pFont(Font &font) : font(font) {} + void constructor(); +}; + +struct pMessageWindow : public pObject { + static MessageWindow::Response information(Window &parent, const string &text, MessageWindow::Buttons buttons); + static MessageWindow::Response question(Window &parent, const string &text, MessageWindow::Buttons buttons); + static MessageWindow::Response warning(Window &parent, const string &text, MessageWindow::Buttons buttons); + static MessageWindow::Response critical(Window &parent, const string &text, MessageWindow::Buttons buttons); +}; + +struct pWindow : public pObject { + Window &window; + GtkWidget *widget; + GtkWidget *menuContainer; + GtkWidget *formContainer; + GtkWidget *statusContainer; + GtkWidget *menu; + GtkWidget *status; + + void append(Layout &layout); + void append(Menu &menu); + void append(Widget &widget); + Geometry frameGeometry(); + bool focused(); + Geometry geometry(); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setFrameGeometry(const Geometry &geometry); + void setFocused(); + void setFullScreen(bool fullScreen); + void setGeometry(const Geometry &geometry); + void setMenuFont(Font &font); + void setMenuVisible(bool visible); + void setResizable(bool resizable); + void setStatusFont(Font &font); + void setStatusText(const string &text); + void setStatusVisible(bool visible); + void setTitle(const string &text); + void setVisible(bool visible); + void setWidgetFont(Font &font); + + pWindow(Window &window) : window(window) {} + void constructor(); + void updateFrameGeometry(); +}; + +struct pAction : public pObject { + Action &action; + GtkWidget *widget; + + void setEnabled(bool enabled); + void setVisible(bool visible); + + pAction(Action &action) : action(action) {} + void constructor(); + virtual void setFont(Font &font); +}; + +struct pMenu : public pAction { + Menu &menu; + GtkWidget *submenu; + + void append(Action &action); + void setText(const string &text); + + pMenu(Menu &menu) : pAction(menu), menu(menu) {} + void constructor(); + void setFont(Font &font); +}; + +struct pMenuSeparator : public pAction { + MenuSeparator &menuSeparator; + + pMenuSeparator(MenuSeparator &menuSeparator) : pAction(menuSeparator), menuSeparator(menuSeparator) {} + void constructor(); +}; + +struct pMenuItem : public pAction { + MenuItem &menuItem; + + void setText(const string &text); + + pMenuItem(MenuItem &menuItem) : pAction(menuItem), menuItem(menuItem) {} + void constructor(); +}; + +struct pMenuCheckItem : public pAction { + MenuCheckItem &menuCheckItem; + + bool checked(); + void setChecked(bool checked); + void setText(const string &text); + + pMenuCheckItem(MenuCheckItem &menuCheckItem) : pAction(menuCheckItem), menuCheckItem(menuCheckItem) {} + void constructor(); +}; + +struct pMenuRadioItem : public pAction { + MenuRadioItem &menuRadioItem; + + bool checked(); + void setChecked(); + void setGroup(const reference_array &group); + void setText(const string &text); + + pMenuRadioItem(MenuRadioItem &menuRadioItem) : pAction(menuRadioItem), menuRadioItem(menuRadioItem) {} + void constructor(); +}; + +struct pWidget : public pObject { + Widget &widget; + GtkWidget *gtkWidget; + pWindow *parentWindow; + + bool enabled(); + void setEnabled(bool enabled); + virtual void setFocused(); + virtual void setFont(Font &font); + void setGeometry(const Geometry &geometry); + void setVisible(bool visible); + + pWidget(Widget &widget) : widget(widget) {} + void constructor(); +}; + +struct pButton : public pWidget { + Button &button; + + void setText(const string &text); + + pButton(Button &button) : pWidget(button), button(button) {} + void constructor(); +}; + +struct pCheckBox : public pWidget { + CheckBox &checkBox; + + bool checked(); + void setChecked(bool checked); + void setText(const string &text); + + pCheckBox(CheckBox &checkBox) : pWidget(checkBox), checkBox(checkBox) {} + void constructor(); +}; + +struct pComboBox : public pWidget { + ComboBox &comboBox; + unsigned itemCounter; + + void append(const string &text); + void reset(); + unsigned selection(); + void setSelection(unsigned row); + + pComboBox(ComboBox &comboBox) : pWidget(comboBox), comboBox(comboBox) {} + void constructor(); +}; + +struct pHexEdit : public pWidget { + HexEdit &hexEdit; + GtkWidget *container; + GtkWidget *subWidget; + GtkWidget *scrollBar; + GtkTextBuffer *textBuffer; + GtkTextMark *textCursor; + + void setColumns(unsigned columns); + void setLength(unsigned length); + void setOffset(unsigned offset); + void setRows(unsigned rows); + void update(); + + pHexEdit(HexEdit &hexEdit) : pWidget(hexEdit), hexEdit(hexEdit) {} + void constructor(); + unsigned cursorPosition(); + bool keyPress(unsigned scancode); + void scroll(unsigned position); + void setCursorPosition(unsigned position); + void setScroll(); + void updateScroll(); +}; + +struct pHorizontalSlider : public pWidget { + HorizontalSlider &horizontalSlider; + + unsigned position(); + void setLength(unsigned length); + void setPosition(unsigned position); + + pHorizontalSlider(HorizontalSlider &horizontalSlider) : pWidget(horizontalSlider), horizontalSlider(horizontalSlider) {} + void constructor(); +}; + +struct pLabel : public pWidget { + Label &label; + + void setText(const string &text); + + pLabel(Label &label) : pWidget(label), label(label) {} + void constructor(); +}; + +struct pLineEdit : public pWidget { + LineEdit &lineEdit; + + void setEditable(bool editable); + void setText(const string &text); + string text(); + + pLineEdit(LineEdit &lineEdit) : pWidget(lineEdit), lineEdit(lineEdit) {} + void constructor(); +}; + +struct pListView : public pWidget { + ListView &listView; + GtkWidget *subWidget; + GtkListStore *store; + struct GtkColumn { + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkWidget *label; + }; + linear_vector column; + + void append(const lstring &text); + void autosizeColumns(); + bool checked(unsigned row); + void modify(unsigned row, const lstring &text); + void modify(unsigned row, unsigned column, const string &text); + void reset(); + optional selection(); + void setCheckable(bool checkable); + void setChecked(unsigned row, bool checked); + void setHeaderText(const lstring &text); + void setHeaderVisible(bool visible); + void setSelection(unsigned row); + + pListView(ListView &listView) : pWidget(listView), listView(listView) {} + void constructor(); + void create(); + void setFocused(); + void setFont(Font &font); +}; + +struct pProgressBar : public pWidget { + ProgressBar &progressBar; + + void setPosition(unsigned position); + + pProgressBar(ProgressBar &progressBar) : pWidget(progressBar), progressBar(progressBar) {} + void constructor(); +}; + +struct pRadioBox : public pWidget { + RadioBox &radioBox; + + bool checked(); + void setChecked(); + void setGroup(const reference_array &group); + void setText(const string &text); + + pRadioBox(RadioBox &radioBox) : pWidget(radioBox), radioBox(radioBox) {} + void constructor(); +}; + +struct pTextEdit : public pWidget { + TextEdit &textEdit; + GtkWidget *subWidget; + GtkTextBuffer *textBuffer; + + void setCursorPosition(unsigned position); + void setEditable(bool editable); + void setText(const string &text); + void setWordWrap(bool wordWrap); + string text(); + + pTextEdit(TextEdit &textEdit) : pWidget(textEdit), textEdit(textEdit) {} + void constructor(); +}; + +struct pVerticalSlider : public pWidget { + VerticalSlider &verticalSlider; + + unsigned position(); + void setLength(unsigned length); + void setPosition(unsigned position); + + pVerticalSlider(VerticalSlider &verticalSlider) : pWidget(verticalSlider), verticalSlider(verticalSlider) {} + void constructor(); +}; + +struct pViewport : public pWidget { + Viewport &viewport; + + uintptr_t handle(); + + pViewport(Viewport &viewport) : pWidget(viewport), viewport(viewport) {} + void constructor(); +}; diff --git a/bsnes/phoenix/gtk/message-window.cpp b/bsnes/phoenix/gtk/message-window.cpp new file mode 100755 index 00000000..d0b4f7b4 --- /dev/null +++ b/bsnes/phoenix/gtk/message-window.cpp @@ -0,0 +1,61 @@ +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, gint response) { + if(response == GTK_RESPONSE_OK) return MessageWindow::Response::Ok; + if(response == GTK_RESPONSE_CANCEL) return MessageWindow::Response::Cancel; + if(response == GTK_RESPONSE_YES) return MessageWindow::Response::Yes; + if(response == GTK_RESPONSE_NO) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response pMessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response pMessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response pMessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response pMessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, buttonsType, "%s", (const char*)text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} diff --git a/bsnes/phoenix/gtk/settings.cpp b/bsnes/phoenix/gtk/settings.cpp new file mode 100755 index 00000000..13ee9dbe --- /dev/null +++ b/bsnes/phoenix/gtk/settings.cpp @@ -0,0 +1,22 @@ +static Settings settings; + +void Settings::load() { + string path = { userpath(), ".config/phoenix/gtk.cfg" }; + configuration::load(path); +} + +void Settings::save() { + string path = { userpath(), ".config/" }; + mkdir(path, 0755); + path.append("phoenix/"); + mkdir(path, 0755); + path.append("gtk.cfg"); + configuration::save(path); +} + +Settings::Settings() { + attach(frameGeometryX = 0, "frameGeometryX"); + attach(frameGeometryY = 0, "frameGeometryY"); + attach(frameGeometryWidth = 0, "frameGeometryWidth"); + attach(frameGeometryHeight = 0, "frameGeometryHeight"); +} diff --git a/bsnes/phoenix/gtk/widget/button.cpp b/bsnes/phoenix/gtk/widget/button.cpp new file mode 100755 index 00000000..75774ddd --- /dev/null +++ b/bsnes/phoenix/gtk/widget/button.cpp @@ -0,0 +1,12 @@ +static void Button_tick(Button *self) { + if(self->onTick) self->onTick(); +} + +void pButton::setText(const string &text) { + gtk_button_set_label(GTK_BUTTON(gtkWidget), text); +} + +void pButton::constructor() { + gtkWidget = gtk_button_new(); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "clicked", G_CALLBACK(Button_tick), (gpointer)&button); +} diff --git a/bsnes/phoenix/gtk/widget/check-box.cpp b/bsnes/phoenix/gtk/widget/check-box.cpp new file mode 100755 index 00000000..bcacb7f3 --- /dev/null +++ b/bsnes/phoenix/gtk/widget/check-box.cpp @@ -0,0 +1,22 @@ +static void CheckBox_tick(CheckBox *self) { + if(self->p.locked == false && self->onTick) self->onTick(); +} + +bool pCheckBox::checked() { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkWidget)); +} + +void pCheckBox::setChecked(bool checked) { + locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), checked); + locked = false; +} + +void pCheckBox::setText(const string &text) { + gtk_button_set_label(GTK_BUTTON(gtkWidget), text); +} + +void pCheckBox::constructor() { + gtkWidget = gtk_check_button_new_with_label(""); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckBox_tick), (gpointer)&checkBox); +} diff --git a/bsnes/phoenix/gtk/widget/combo-box.cpp b/bsnes/phoenix/gtk/widget/combo-box.cpp new file mode 100755 index 00000000..72338cca --- /dev/null +++ b/bsnes/phoenix/gtk/widget/combo-box.cpp @@ -0,0 +1,33 @@ +static void ComboBox_change(ComboBox *self) { + if(self->p.locked == false && self->onChange) self->onChange(); +} + +void pComboBox::append(const string &text) { + gtk_combo_box_append_text(GTK_COMBO_BOX(gtkWidget), text); + if(itemCounter++ == 0) setSelection(0); +} + +void pComboBox::reset() { + locked = true; + for(signed n = itemCounter - 1; n >= 0; n--) { + gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), n); + } + itemCounter = 0; + locked = false; +} + +unsigned pComboBox::selection() { + return gtk_combo_box_get_active(GTK_COMBO_BOX(gtkWidget)); +} + +void pComboBox::setSelection(unsigned row) { + locked = true; + gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), row); + locked = false; +} + +void pComboBox::constructor() { + itemCounter = 0; + gtkWidget = gtk_combo_box_new_text(); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "changed", G_CALLBACK(ComboBox_change), (gpointer)&comboBox); +} diff --git a/bsnes/phoenix/gtk/widget/hex-edit.cpp b/bsnes/phoenix/gtk/widget/hex-edit.cpp new file mode 100755 index 00000000..481ab77d --- /dev/null +++ b/bsnes/phoenix/gtk/widget/hex-edit.cpp @@ -0,0 +1,246 @@ +static bool HexEdit_keyPress(GtkWidget *widget, GdkEventKey *event, HexEdit *self) { + return self->p.keyPress(event->keyval); +} + +static bool HexEdit_scroll(GtkRange *range, GtkScrollType scroll, gdouble value, HexEdit *self) { + self->p.scroll((unsigned)value); + return false; +} + +void pHexEdit::setColumns(unsigned columns) { + setScroll(); + update(); +} + +void pHexEdit::setLength(unsigned length) { + setScroll(); + update(); +} + +void pHexEdit::setOffset(unsigned offset) { + setScroll(); + updateScroll(); + update(); +} + +void pHexEdit::setRows(unsigned rows) { + setScroll(); + update(); +} + +void pHexEdit::update() { + if(!hexEdit.onRead) { + gtk_text_buffer_set_text(textBuffer, "", -1); + return; + } + + unsigned position = cursorPosition(); + + string output; + unsigned offset = hexEdit.state.offset; + for(unsigned row = 0; row < hexEdit.state.rows; row++) { + output.append(hex<8>(offset)); + output.append(" "); + + string hexdata; + string ansidata = " "; + for(unsigned column = 0; column < hexEdit.state.columns; column++) { + if(offset < hexEdit.state.length) { + uint8_t data = hexEdit.onRead(offset++); + hexdata.append(hex<2>(data)); + hexdata.append(" "); + char buffer[2] = { data >= 0x20 && data <= 0x7e ? (char)data : '.', 0 }; + ansidata.append(buffer); + } else { + hexdata.append(" "); + ansidata.append(" "); + } + } + + output.append(hexdata); + output.append(ansidata); + if(offset >= hexEdit.state.length) break; + if(row != hexEdit.state.rows - 1) output.append("\n"); + } + + gtk_text_buffer_set_text(textBuffer, output, -1); + if(position == 0) position = 10; //start at first position where hex values can be entered + setCursorPosition(position); +} + +void pHexEdit::constructor() { + gtkWidget = gtk_hbox_new(false, 0); + + container = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(container), GTK_POLICY_NEVER, GTK_POLICY_NEVER); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(container), GTK_SHADOW_ETCHED_IN); + + subWidget = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), false); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_NONE); + gtk_container_add(GTK_CONTAINER(container), subWidget); + g_signal_connect(G_OBJECT(subWidget), "key-press-event", G_CALLBACK(HexEdit_keyPress), (gpointer)&hexEdit); + + scrollBar = gtk_vscrollbar_new((GtkAdjustment*)0); + gtk_range_set_range(GTK_RANGE(scrollBar), 0, 255); + gtk_range_set_increments(GTK_RANGE(scrollBar), 1, 16); + gtk_widget_set_sensitive(scrollBar, false); + g_signal_connect(G_OBJECT(scrollBar), "change-value", G_CALLBACK(HexEdit_scroll), (gpointer)&hexEdit); + + gtk_box_pack_start(GTK_BOX(gtkWidget), container, true, true, 0); + gtk_box_pack_start(GTK_BOX(gtkWidget), scrollBar, false, false, 1); + + textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget)); + textCursor = gtk_text_buffer_get_mark(textBuffer, "insert"); + + gtk_widget_show(scrollBar); + gtk_widget_show(subWidget); + gtk_widget_show(container); +} + +unsigned pHexEdit::cursorPosition() { + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(textBuffer, &iter, textCursor); + return gtk_text_iter_get_offset(&iter); +} + +bool pHexEdit::keyPress(unsigned scancode) { + if(!hexEdit.onRead) return false; + + unsigned position = cursorPosition(); + unsigned lineWidth = 10 + (hexEdit.state.columns * 3) + 1 + hexEdit.state.columns + 1; + unsigned cursorY = position / lineWidth; + unsigned cursorX = position % lineWidth; + + if(scancode == GDK_Home) { + setCursorPosition(cursorY * lineWidth + 10); + return true; + } + + if(scancode == GDK_End) { + setCursorPosition(cursorY * lineWidth + 10 + (hexEdit.state.columns * 3 - 1)); + return true; + } + + if(scancode == GDK_Up) { + if(cursorY != 0) return false; + + signed newOffset = hexEdit.state.offset - hexEdit.state.columns; + if(newOffset >= 0) { + hexEdit.setOffset(newOffset); + update(); + } + return true; + } + + if(scancode == GDK_Down) { + if(cursorY != hexEdit.state.rows - 1) return false; + + signed newOffset = hexEdit.state.offset + hexEdit.state.columns; + if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) { + hexEdit.setOffset(newOffset); + update(); + } + return true; + } + + if(scancode == GDK_Page_Up) { + signed newOffset = hexEdit.state.offset - hexEdit.state.columns * hexEdit.state.rows; + if(newOffset >= 0) { + hexEdit.setOffset(newOffset); + } else { + hexEdit.setOffset(0); + } + update(); + return true; + } + + if(scancode == GDK_Page_Down) { + signed newOffset = hexEdit.state.offset + hexEdit.state.columns * hexEdit.state.rows; + for(unsigned n = 0; n < hexEdit.state.rows; n++) { + if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) { + hexEdit.setOffset(newOffset); + update(); + break; + } + newOffset -= hexEdit.state.columns; + } + return true; + } + + //convert scancode to hex nibble + if(scancode >= '0' && scancode <= '9') scancode = scancode - '0'; + else if(scancode >= 'A' && scancode <= 'F') scancode = scancode - 'A' + 10; + else if(scancode >= 'a' && scancode <= 'f') scancode = scancode - 'a' + 10; + else return false; //not a valid hex value + + if(cursorX >= 10) { + //not on an offset + cursorX -= 10; + if((cursorX % 3) != 2) { + //not on a space + bool cursorNibble = (cursorX % 3) == 1; //0 = high, 1 = low + cursorX /= 3; + if(cursorX < hexEdit.state.columns) { + //not in ANSI region + unsigned offset = hexEdit.state.offset + (cursorY * hexEdit.state.columns + cursorX); + + if(offset >= hexEdit.state.length) return false; //do not edit past end of data + uint8_t data = hexEdit.onRead(offset); + + //write modified value + if(cursorNibble == 1) { + data = (data & 0xf0) | (scancode << 0); + } else { + data = (data & 0x0f) | (scancode << 4); + } + if(hexEdit.onWrite) hexEdit.onWrite(offset, data); + + //auto-advance cursor to next nibble/byte + position++; + if(cursorNibble && cursorX != hexEdit.state.columns - 1) position++; + setCursorPosition(position); + + //refresh output to reflect modified data + update(); + } + } + } + + return true; +} + +void pHexEdit::scroll(unsigned position) { + unsigned rows = hexEdit.state.length / hexEdit.state.columns; + if(position >= rows) position = rows - 1; + hexEdit.setOffset(position * hexEdit.state.columns); +} + +void pHexEdit::setCursorPosition(unsigned position) { + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(textBuffer, &iter, textCursor); + + //GTK+ will throw many errors to the terminal if you set iterator past end of buffer + GtkTextIter endIter; + gtk_text_buffer_get_end_iter(textBuffer, &iter); + unsigned endPosition = gtk_text_iter_get_offset(&iter); + + gtk_text_iter_set_offset(&iter, min(position, endPosition)); + gtk_text_buffer_place_cursor(textBuffer, &iter); +} + +void pHexEdit::setScroll() { + unsigned rows = hexEdit.state.length / hexEdit.state.columns; + if(rows) rows--; + if(rows) { + gtk_range_set_range(GTK_RANGE(scrollBar), 0, rows); + gtk_widget_set_sensitive(scrollBar, true); + } else { + gtk_widget_set_sensitive(scrollBar, false); + } +} + +void pHexEdit::updateScroll() { + unsigned row = hexEdit.state.offset / hexEdit.state.columns; + gtk_range_set_value(GTK_RANGE(scrollBar), row); +} diff --git a/bsnes/phoenix/gtk/widget/horizontal-slider.cpp b/bsnes/phoenix/gtk/widget/horizontal-slider.cpp new file mode 100755 index 00000000..44f6614d --- /dev/null +++ b/bsnes/phoenix/gtk/widget/horizontal-slider.cpp @@ -0,0 +1,24 @@ +static void HorizontalSlider_change(HorizontalSlider *self) { + if(self->state.position == self->position()) return; + self->state.position = self->position(); + if(self->onChange) self->onChange(); +} + +unsigned pHorizontalSlider::position() { + return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget)); +} + +void pHorizontalSlider::setLength(unsigned length) { + length += length == 0; + gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1); +} + +void pHorizontalSlider::setPosition(unsigned position) { + gtk_range_set_value(GTK_RANGE(gtkWidget), position); +} + +void pHorizontalSlider::constructor() { + gtkWidget = gtk_hscale_new_with_range(0, 100, 1); + gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)&horizontalSlider); +} diff --git a/bsnes/phoenix/gtk/widget/label.cpp b/bsnes/phoenix/gtk/widget/label.cpp new file mode 100755 index 00000000..bcf38121 --- /dev/null +++ b/bsnes/phoenix/gtk/widget/label.cpp @@ -0,0 +1,8 @@ +void pLabel::setText(const string &text) { + gtk_label_set_text(GTK_LABEL(gtkWidget), text); +} + +void pLabel::constructor() { + gtkWidget = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(gtkWidget), 0.0, 0.5); +} diff --git a/bsnes/phoenix/gtk/widget/line-edit.cpp b/bsnes/phoenix/gtk/widget/line-edit.cpp new file mode 100755 index 00000000..65359fbd --- /dev/null +++ b/bsnes/phoenix/gtk/widget/line-edit.cpp @@ -0,0 +1,27 @@ +static void LineEdit_activate(LineEdit *self) { + if(self->onActivate) self->onActivate(); +} + +static void LineEdit_change(LineEdit *self) { + if(self->p.locked == false && self->onChange) self->onChange(); +} + +void pLineEdit::setEditable(bool editable) { + gtk_entry_set_editable(GTK_ENTRY(gtkWidget), editable); +} + +void pLineEdit::setText(const string &text) { + locked = true; + gtk_entry_set_text(GTK_ENTRY(gtkWidget), text); + locked = false; +} + +string pLineEdit::text() { + return gtk_entry_get_text(GTK_ENTRY(gtkWidget)); +} + +void pLineEdit::constructor() { + gtkWidget = gtk_entry_new(); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "activate", G_CALLBACK(LineEdit_activate), (gpointer)&lineEdit); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "changed", G_CALLBACK(LineEdit_change), (gpointer)&lineEdit); +} diff --git a/bsnes/phoenix/gtk/widget/list-view.cpp b/bsnes/phoenix/gtk/widget/list-view.cpp new file mode 100755 index 00000000..82327a01 --- /dev/null +++ b/bsnes/phoenix/gtk/widget/list-view.cpp @@ -0,0 +1,195 @@ +static void ListView_activate(ListView *self) { + signed selection = -1; + if(self->onActivate) self->onActivate(); +} + +static void ListView_change(ListView *self) { + signed selection = -1; + self->state.selection = self->selection(); + if(self->onChange) self->onChange(); +} + +static void ListView_tick(GtkCellRendererToggle *cell, gchar *path_string, ListView *self) { + unsigned row = decimal(path_string); + self->setChecked(row, !self->checked(row)); + if(self->onTick) self->onTick(row); +} + +void pListView::append(const lstring &text) { + GtkTreeIter iter; + gtk_list_store_append(store, &iter); + foreach(item, text, n) gtk_list_store_set(store, &iter, 1 + n, (const char*)item, -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)); + 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 pListView::modify(unsigned row, const lstring &text) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); + GtkTreeIter iter; + for(unsigned i = 0; i <= row; i++) { + if(i == 0) gtk_tree_model_get_iter_first(model, &iter); + else gtk_tree_model_iter_next(model, &iter); + } + foreach(item, text, n) gtk_list_store_set(store, &iter, 1 + n, (const char*)item, -1); +} + +void pListView::modify(unsigned row, unsigned column, const string &text) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); + GtkTreeIter iter; + for(unsigned i = 0; i <= row; i++) { + if(i == 0) gtk_tree_model_get_iter_first(model, &iter); + else gtk_tree_model_iter_next(model, &iter); + } + gtk_list_store_set(store, &iter, 1 + column, (const char*)text, -1); +} + +void pListView::reset() { + listView.state.selection = { false, 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); +} + +optional 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; + if(gtk_tree_model_get_iter_first(model, &iter) == false) return { false, 0 }; + if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, 0 }; + for(unsigned n = 1;; n++) { + if(gtk_tree_model_iter_next(model, &iter) == false) return { false, 0 }; + if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, n }; + } + return { false, 0 }; +} + +void pListView::setCheckable(bool checkable) { + gtk_tree_view_column_set_visible(column[0].column, checkable); +} + +void pListView::setChecked(unsigned row, bool checked) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(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); +} + +void pListView::setHeaderText(const lstring &text) { + create(); +} + +void pListView::setHeaderVisible(bool visible) { + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(subWidget), visible); +} + +void pListView::setSelection(unsigned row) { + signed current = -1; + if(auto position = selection()) current = position(); + 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; + if(gtk_tree_model_get_iter_first(model, &iter) == false) return; + if(row == 0) { + gtk_tree_selection_select_iter(selection, &iter); + return; + } + for(unsigned n = 1;; n++) { + if(gtk_tree_model_iter_next(model, &iter) == false) return; + if(row == n) { + gtk_tree_selection_select_iter(selection, &iter); + return; + } + } +} + +void pListView::constructor() { + gtkWidget = 0; + subWidget = 0; + create(); +} + +void pListView::create() { + if(subWidget) gtk_widget_destroy(subWidget); + if(gtkWidget) gtk_widget_destroy(gtkWidget); + + 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; + headerText.append(""); //checkbox column + foreach(headerItem, listView.state.headerText) headerText.append(headerItem); + if(headerText.size() == 1) headerText.append(""); + + GType *v = (GType*)malloc(headerText.size() * sizeof(GType)); + foreach(header, headerText, n) v[n] = (n == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING); + store = gtk_list_store_newv(headerText.size(), v); + free(v); + + subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget); + g_object_unref(G_OBJECT(store)); + + foreach(header, headerText, n) { + if(n == 0) { + column[n].renderer = gtk_cell_renderer_toggle_new(); + column[n].column = gtk_tree_view_column_new_with_attributes("", column[n].renderer, "active", n, (void*)0); + gtk_tree_view_column_set_resizable(column[n].column, false); + gtk_tree_view_column_set_visible(column[n].column, false); + g_signal_connect(column[n].renderer, "toggled", G_CALLBACK(ListView_tick), (gpointer)&listView); + } else { + column[n].renderer = gtk_cell_renderer_text_new(); + column[n].column = gtk_tree_view_column_new_with_attributes("", column[n].renderer, "text", n, (void*)0); + gtk_tree_view_column_set_resizable(column[n].column, true); + } + column[n].label = gtk_label_new(header); + gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(column[n].column), column[n].label); + gtk_tree_view_append_column(GTK_TREE_VIEW(subWidget), column[n].column); + gtk_widget_show(column[n].label); + } + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(subWidget), headerText.size() >= 3); //two or more columns + checkbox column + gtk_tree_view_set_search_column(GTK_TREE_VIEW(subWidget), 1); + + 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); + + setHeaderVisible(listView.state.headerVisible); + setCheckable(listView.state.checkable); + foreach(text, listView.state.text) append(text); + foreach(checked, listView.state.checked, n) setChecked(n, checked); + if(auto selection = listView.state.selection) setSelection(selection()); + autosizeColumns(); + + gtk_widget_show(subWidget); +} + +void pListView::setFocused() { + gtk_widget_grab_focus(subWidget); +} + +void pListView::setFont(Font &font) { + pWidget::setFont(font); + for(unsigned n = 0; n < 1 + listView.state.headerText.size(); n++) { + gtk_widget_modify_font(column[n].label, font.p.gtkFont); + } +} diff --git a/bsnes/phoenix/gtk/widget/progress-bar.cpp b/bsnes/phoenix/gtk/widget/progress-bar.cpp new file mode 100755 index 00000000..96959b62 --- /dev/null +++ b/bsnes/phoenix/gtk/widget/progress-bar.cpp @@ -0,0 +1,8 @@ +void pProgressBar::setPosition(unsigned position) { + position = position <= 100 ? position : 0; + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkWidget), (double)position / 100.0); +} + +void pProgressBar::constructor() { + gtkWidget = gtk_progress_bar_new(); +} diff --git a/bsnes/phoenix/gtk/widget/radio-box.cpp b/bsnes/phoenix/gtk/widget/radio-box.cpp new file mode 100755 index 00000000..2fb7d38f --- /dev/null +++ b/bsnes/phoenix/gtk/widget/radio-box.cpp @@ -0,0 +1,32 @@ +static void RadioBox_tick(RadioBox *self) { + if(self->p.locked == false && self->checked() && self->onTick) self->onTick(); +} + +bool pRadioBox::checked() { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkWidget)); +} + +void pRadioBox::setChecked() { + locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), true); + locked = false; +} + +void pRadioBox::setGroup(const reference_array &group) { + foreach(item, group, n) { + if(n == 0) continue; + GSList *currentGroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(group[0].p.gtkWidget)); + if(currentGroup != gtk_radio_button_get_group(GTK_RADIO_BUTTON(gtkWidget))) { + gtk_radio_button_set_group(GTK_RADIO_BUTTON(gtkWidget), currentGroup); + } + } +} + +void pRadioBox::setText(const string &text) { + gtk_button_set_label(GTK_BUTTON(gtkWidget), text); +} + +void pRadioBox::constructor() { + gtkWidget = gtk_radio_button_new_with_label(0, ""); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)&radioBox); +} diff --git a/bsnes/phoenix/gtk/widget/text-edit.cpp b/bsnes/phoenix/gtk/widget/text-edit.cpp new file mode 100755 index 00000000..cf6494f8 --- /dev/null +++ b/bsnes/phoenix/gtk/widget/text-edit.cpp @@ -0,0 +1,48 @@ +static void TextEdit_change(TextEdit *self) { + if(self->p.locked == false && self->onChange) self->onChange(); +} + +void pTextEdit::setCursorPosition(unsigned position) { + GtkTextMark *mark = gtk_text_buffer_get_mark(textBuffer, "insert"); + GtkTextIter iter; + gtk_text_buffer_get_end_iter(textBuffer, &iter); + gtk_text_iter_set_offset(&iter, min(position, gtk_text_iter_get_offset(&iter))); + gtk_text_buffer_place_cursor(textBuffer, &iter); + gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark); +} + +void pTextEdit::setEditable(bool editable) { + gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), editable); +} + +void pTextEdit::setText(const string &text) { + locked = true; + gtk_text_buffer_set_text(textBuffer, text, -1); + locked = false; +} + +void pTextEdit::setWordWrap(bool wordWrap) { + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE); +} + +string pTextEdit::text() { + GtkTextIter start, end; + gtk_text_buffer_get_start_iter(textBuffer, &start); + gtk_text_buffer_get_end_iter(textBuffer, &end); + char *temp = gtk_text_buffer_get_text(textBuffer, &start, &end, true); + string text = temp; + g_free(temp); + return text; +} + +void pTextEdit::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); + subWidget = gtk_text_view_new(); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_WORD_CHAR); + gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget); + textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget)); + g_signal_connect_swapped(G_OBJECT(textBuffer), "changed", G_CALLBACK(TextEdit_change), (gpointer)&textEdit); + gtk_widget_show(subWidget); +} diff --git a/bsnes/phoenix/gtk/widget/vertical-slider.cpp b/bsnes/phoenix/gtk/widget/vertical-slider.cpp new file mode 100755 index 00000000..dbd4347c --- /dev/null +++ b/bsnes/phoenix/gtk/widget/vertical-slider.cpp @@ -0,0 +1,24 @@ +static void VerticalSlider_change(VerticalSlider *self) { + if(self->state.position == self->position()) return; + self->state.position = self->position(); + if(self->onChange) self->onChange(); +} + +unsigned pVerticalSlider::position() { + return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget)); +} + +void pVerticalSlider::setLength(unsigned length) { + length += length == 0; + gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1); +} + +void pVerticalSlider::setPosition(unsigned position) { + gtk_range_set_value(GTK_RANGE(gtkWidget), position); +} + +void pVerticalSlider::constructor() { + gtkWidget = gtk_vscale_new_with_range(0, 100, 1); + gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)&verticalSlider); +} diff --git a/bsnes/phoenix/gtk/widget/viewport.cpp b/bsnes/phoenix/gtk/widget/viewport.cpp new file mode 100755 index 00000000..fd3bae57 --- /dev/null +++ b/bsnes/phoenix/gtk/widget/viewport.cpp @@ -0,0 +1,15 @@ +uintptr_t pViewport::handle() { + return GDK_WINDOW_XID(gtkWidget->window); +} + +void pViewport::constructor() { + gtkWidget = gtk_drawing_area_new(); +//gtk_widget_set_double_buffered(gtkWidget, false); + + GdkColor color; + color.pixel = 0; + color.red = 0; + color.green = 0; + color.blue = 0; + gtk_widget_modify_bg(gtkWidget, GTK_STATE_NORMAL, &color); +} diff --git a/bsnes/phoenix/gtk/widget/widget.cpp b/bsnes/phoenix/gtk/widget/widget.cpp new file mode 100755 index 00000000..d26d0c6c --- /dev/null +++ b/bsnes/phoenix/gtk/widget/widget.cpp @@ -0,0 +1,40 @@ +static void Widget_setFont(GtkWidget *widget, gpointer font) { + if(font == 0) return; + gtk_widget_modify_font(widget, (PangoFontDescription*)font); + if(GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Widget_setFont, font); + } +} + +bool pWidget::enabled() { + return gtk_widget_get_sensitive(gtkWidget); +} + +void pWidget::setEnabled(bool enabled) { + gtk_widget_set_sensitive(gtkWidget, enabled); +} + +void pWidget::setFocused() { + gtk_widget_grab_focus(gtkWidget); +} + +void pWidget::setFont(Font &font) { + Widget_setFont(gtkWidget, font.p.gtkFont); +} + +void pWidget::setGeometry(const Geometry &geometry) { + if(parentWindow) gtk_fixed_move(GTK_FIXED(parentWindow->formContainer), gtkWidget, geometry.x, geometry.y); + unsigned width = (signed)geometry.width <= 0 ? 1U : geometry.width; + unsigned height = (signed)geometry.height <= 0 ? 1U : geometry.height; + gtk_widget_set_size_request(gtkWidget, width, height); +} + +void pWidget::setVisible(bool visible) { + if(widget.state.abstract) visible = false; + gtk_widget_set_visible(gtkWidget, visible); +} + +void pWidget::constructor() { + parentWindow = 0; + if(widget.state.abstract) gtkWidget = gtk_label_new(""); +} diff --git a/bsnes/phoenix/gtk/window.cpp b/bsnes/phoenix/gtk/window.cpp new file mode 100755 index 00000000..677a854c --- /dev/null +++ b/bsnes/phoenix/gtk/window.cpp @@ -0,0 +1,232 @@ +static void Action_setFont(GtkWidget *widget, gpointer font); +static void Widget_setFont(GtkWidget *widget, gpointer font); + +static gint Window_close(Window *window) { + if(window->onClose) window->onClose(); + window->setVisible(false); + return true; +} + +static gboolean Window_configure(GtkWindow *widget, GdkEvent *event, Window *window) { + if(gtk_widget_get_realized(window->p.widget) == false) return false; + window->p.updateFrameGeometry(); + + signed eventX = event->configure.x, eventY = event->configure.y; + unsigned eventWidth = event->configure.width, eventHeight = event->configure.height; + + if(window->p.locked == false && window->state.fullScreen == false) { + if(window->state.geometry.x != eventX || window->state.geometry.y != eventY) { + window->state.geometry.x = eventX; + window->state.geometry.y = eventY; + } + } + + if(window->onMove) window->onMove(); + + eventHeight -= window->state.menuVisible ? window->p.menu->allocation.height : 0; + eventHeight -= window->state.statusVisible ? window->p.status->allocation.height : 0; + + if(window->p.locked == false && window->state.fullScreen == false) { + if(window->state.geometry.width != eventWidth || window->state.geometry.height != eventHeight) { + window->state.geometry.width = eventWidth; + window->state.geometry.height = eventHeight; + } + } + + foreach(layout, window->state.layout) { + Geometry geometry = window->geometry(); + geometry.x = geometry.y = 0; + layout.setGeometry(geometry); + } + + if(window->onSize) window->onSize(); + return false; +} + +void pWindow::append(Layout &layout) { + layout.setParent(window); + Geometry geometry = this->geometry(); + geometry.x = geometry.y = 0; + layout.setGeometry(geometry); +} + +void pWindow::append(Menu &subMenu) { + if(window.state.menuFont) subMenu.p.setFont(*window.state.menuFont); + gtk_menu_bar_append(menu, subMenu.p.widget); + gtk_widget_show(subMenu.p.widget); +} + +void pWindow::append(Widget &widget) { + widget.p.parentWindow = this; + if(!widget.state.font && window.state.widgetFont) { + widget.setFont(*window.state.widgetFont); + } + gtk_fixed_put(GTK_FIXED(formContainer), widget.p.gtkWidget, 0, 0); + widget.setVisible(); +} + +Geometry pWindow::frameGeometry() { + if(window.state.fullScreen == true) return { 0, 0, OS::desktopWidth(), OS::desktopHeight() }; + return { + window.state.geometry.x - settings.frameGeometryX, + window.state.geometry.y - settings.frameGeometryY, + window.state.geometry.width + settings.frameGeometryWidth, + window.state.geometry.height + settings.frameGeometryHeight + }; +} + +bool pWindow::focused() { + return gtk_window_is_active(GTK_WINDOW(widget)); +} + +Geometry pWindow::geometry() { + if(window.state.fullScreen == true) { + unsigned menuHeight = 0, statusHeight = 0; + if(window.state.menuVisible) menuHeight = menu->allocation.height; + if(window.state.statusVisible) statusHeight = menu->allocation.height; + return { 0, menuHeight, OS::desktopWidth(), OS::desktopHeight() - menuHeight - statusHeight }; + } + return window.state.geometry; +} + +void pWindow::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + GdkColor color; + color.pixel = (red << 16) | (green << 8) | (blue << 0); + color.red = (red << 8) | (red << 0); + color.green = (green << 8) | (green << 0); + color.blue = (blue << 8) | (blue << 0); + gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &color); +} + +void pWindow::setFrameGeometry(const Geometry &geometry) { + window.setGeometry({ + geometry.x + settings.frameGeometryX, geometry.y + settings.frameGeometryY, + geometry.width - settings.frameGeometryWidth, geometry.height - settings.frameGeometryHeight + }); +} + +void pWindow::setFocused() { + gtk_window_present(GTK_WINDOW(widget)); +} + +void pWindow::setFullScreen(bool fullScreen) { + if(fullScreen == false) { + gtk_window_unfullscreen(GTK_WINDOW(widget)); + gtk_window_set_resizable(GTK_WINDOW(widget), window.state.resizable); + gtk_window_set_decorated(GTK_WINDOW(widget), true); + locked = true; + for(unsigned n = 0; n < 4; n++) { + setGeometry(window.state.geometry); + gtk_widget_set_size_request(widget, -1, -1); + OS::process(); + usleep(2000); + } + locked = false; + } else { + gtk_window_fullscreen(GTK_WINDOW(widget)); + gtk_window_set_decorated(GTK_WINDOW(widget), false); + gtk_widget_set_size_request(widget, OS::desktopWidth(), OS::desktopHeight()); + gtk_window_set_resizable(GTK_WINDOW(widget), false); + } +} + +void pWindow::setGeometry(const Geometry &geometry) { + gtk_window_move(GTK_WINDOW(widget), geometry.x - settings.frameGeometryX, geometry.y - settings.frameGeometryY); + gtk_window_resize(GTK_WINDOW(widget), 1, 1); + gtk_widget_set_size_request(formContainer, geometry.width, geometry.height); + foreach(layout, window.state.layout) { + Geometry geometry = this->geometry(); + geometry.x = geometry.y = 0; + layout.setGeometry(geometry); + } +} + +void pWindow::setMenuFont(Font &font) { + foreach(item, window.state.menu) item.p.setFont(font); +} + +void pWindow::setMenuVisible(bool visible) { + gtk_widget_set_visible(menu, visible); +} + +void pWindow::setResizable(bool resizable) { + gtk_window_set_resizable(GTK_WINDOW(widget), resizable); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), resizable); +} + +void pWindow::setStatusFont(Font &font) { + Widget_setFont(status, (gpointer)font.p.gtkFont); +} + +void pWindow::setStatusText(const string &text) { + gtk_statusbar_pop(GTK_STATUSBAR(status), 1); + gtk_statusbar_push(GTK_STATUSBAR(status), 1, text); +} + +void pWindow::setStatusVisible(bool visible) { + gtk_widget_set_visible(status, visible); +} + +void pWindow::setTitle(const string &text) { + gtk_window_set_title(GTK_WINDOW(widget), text); +} + +void pWindow::setVisible(bool visible) { + gtk_widget_set_visible(widget, visible); +} + +void pWindow::setWidgetFont(Font &font) { + foreach(item, window.state.widget) { + if(!item.state.font) item.setFont(font); + } +} + +void pWindow::constructor() { + widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_resizable(GTK_WINDOW(widget), true); + gtk_widget_set_app_paintable(widget, true); + gtk_widget_add_events(widget, GDK_CONFIGURE); + + menuContainer = gtk_vbox_new(false, 0); + gtk_container_add(GTK_CONTAINER(widget), menuContainer); + gtk_widget_show(menuContainer); + + menu = gtk_menu_bar_new(); + gtk_box_pack_start(GTK_BOX(menuContainer), menu, false, false, 0); + + formContainer = gtk_fixed_new(); + gtk_box_pack_start(GTK_BOX(menuContainer), formContainer, true, true, 0); + gtk_widget_show(formContainer); + + statusContainer = gtk_event_box_new(); + status = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), true); + gtk_container_add(GTK_CONTAINER(statusContainer), status); + gtk_box_pack_start(GTK_BOX(menuContainer), statusContainer, false, false, 0); + gtk_widget_show(statusContainer); + + setTitle(""); + setGeometry(window.state.geometry); + + g_signal_connect_swapped(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window); + g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window); +} + +void pWindow::updateFrameGeometry() { + #if defined(PLATFORM_X) + Display *display = XOpenDisplay(0); + XWindowAttributes attributes, parentAttributes; + XGetWindowAttributes(display, GDK_WINDOW_XID(widget->window), &attributes); + X11Window rootWindow, parentWindow, *childWindow = 0; + unsigned int childCount; + XQueryTree(display, GDK_WINDOW_XID(widget->window), &rootWindow, &parentWindow, &childWindow, &childCount); + XGetWindowAttributes(display, parentWindow, &parentAttributes); + if(childWindow) XFree(childWindow); + XCloseDIsplay(display); + + settings.frameGeometryX = attributes.x; + settings.frameGeometryY = attributes.y; + settings.frameGeometryWidth = parentAttributes.width - attributes.width; + settings.frameGeometryHeight = parentAttributes.height - attributes.height; + #endif +} diff --git a/bsnes/phoenix/phoenix.cpp b/bsnes/phoenix/phoenix.cpp index f79de775..28307897 100755 --- a/bsnes/phoenix/phoenix.cpp +++ b/bsnes/phoenix/phoenix.cpp @@ -13,6 +13,17 @@ #elif defined(PHOENIX_QT) #include #include +#elif defined(PHOENIX_GTK) + #define None X11None + #define Window X11Window + + #include + #include + #include + #include + + #undef None + #undef Window #elif defined(PHOENIX_REFERENCE) #else #error "phoenix: unrecognized target" diff --git a/bsnes/phoenix/qt/qt.moc b/bsnes/phoenix/qt/qt.moc index 29b94335..2ddb8e4f 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: Fri Feb 18 07:05:10 2011 +** Created: Tue Feb 22 04:37:04 2011 ** by: The Qt Meta Object Compiler version 62 (Qt 4.6.2) ** ** WARNING! All changes made in this file will be lost! diff --git a/bsnes/phoenix/windows/widget/combo-box.cpp b/bsnes/phoenix/windows/widget/combo-box.cpp index 90d089ed..c574abb0 100755 --- a/bsnes/phoenix/windows/widget/combo-box.cpp +++ b/bsnes/phoenix/windows/widget/combo-box.cpp @@ -19,6 +19,14 @@ void pComboBox::constructor() { setParent(Window::None); } +void pComboBox::setGeometry(const Geometry &geometry) { + SetWindowPos(hwnd, NULL, geometry.x, geometry.y, geometry.width, 200, SWP_NOZORDER); + RECT rc; + GetWindowRect(hwnd, &rc); + unsigned adjustedHeight = geometry.height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0)); + SendMessage(hwnd, CB_SETITEMHEIGHT, (WPARAM)-1, adjustedHeight); +} + void pComboBox::setParent(Window &parent) { if(hwnd) DestroyWindow(hwnd); hwnd = CreateWindow( diff --git a/bsnes/phoenix/windows/widget/list-view.cpp b/bsnes/phoenix/windows/widget/list-view.cpp index d5c6b03a..402463c5 100755 --- a/bsnes/phoenix/windows/widget/list-view.cpp +++ b/bsnes/phoenix/windows/widget/list-view.cpp @@ -102,6 +102,11 @@ void pListView::constructor() { setParent(Window::None); } +void pListView::setGeometry(const Geometry &geometry) { + pWidget::setGeometry(geometry); + autosizeColumns(); +} + void pListView::setParent(Window &parent) { if(hwnd) DestroyWindow(hwnd); hwnd = CreateWindowEx( diff --git a/bsnes/phoenix/windows/widget/widget.cpp b/bsnes/phoenix/windows/widget/widget.cpp index 5a8df452..062da650 100755 --- a/bsnes/phoenix/windows/widget/widget.cpp +++ b/bsnes/phoenix/windows/widget/widget.cpp @@ -1,14 +1,17 @@ bool pWidget::enabled() { - return false; + return IsWindowEnabled(hwnd); } void pWidget::setEnabled(bool enabled) { + EnableWindow(hwnd, enabled); } void pWidget::setFocused() { + SetFocus(hwnd); } void pWidget::setFont(Font &font) { + SendMessage(hwnd, WM_SETFONT, (WPARAM)font.p.hfont, 0); } void pWidget::setGeometry(const Geometry &geometry) { @@ -16,10 +19,13 @@ void pWidget::setGeometry(const Geometry &geometry) { } void pWidget::setVisible(bool visible) { + if(widget.state.abstract) visible = false; + ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE); } void pWidget::constructor() { hwnd = 0; + if(widget.state.abstract) setParent(Window::None); } void pWidget::setDefaultFont() { @@ -31,4 +37,7 @@ void pWidget::setDefaultFont() { } void pWidget::setParent(Window &parent) { + if(hwnd) DestroyWindow(hwnd); + hwnd = CreateWindow(L"phoenix_label", L"", WS_CHILD, 0, 0, 0, 0, parent.p.hwnd, (HMENU)id, GetModuleHandle(0), 0); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&widget); } diff --git a/bsnes/phoenix/windows/window.cpp b/bsnes/phoenix/windows/window.cpp index a82d5945..c77df815 100755 --- a/bsnes/phoenix/windows/window.cpp +++ b/bsnes/phoenix/windows/window.cpp @@ -59,6 +59,16 @@ void pWindow::setFocused() { } void pWindow::setFullScreen(bool fullScreen) { + locked = true; + if(fullScreen == false) { + SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | (window.state.resizable ? ResizableStyle : FixedStyle)); + setGeometry(window.state.geometry); + } else { + SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); + Geometry margin = frameMargin(); + setGeometry({ margin.x, margin.y, GetSystemMetrics(SM_CXSCREEN) - margin.width, GetSystemMetrics(SM_CYSCREEN) - margin.height }); + } + locked = false; } void pWindow::setGeometry(const Geometry &geometry) { @@ -95,7 +105,7 @@ void pWindow::setResizable(bool resizable) { } void pWindow::setStatusFont(Font &font) { - SendMessage(hwnd, WM_SETFONT, (WPARAM)font.p.hfont, 0); + SendMessage(hstatus, WM_SETFONT, (WPARAM)font.p.hfont, 0); } void pWindow::setStatusText(const string &text) { @@ -138,8 +148,10 @@ void pWindow::constructor() { } Geometry pWindow::frameMargin() { + unsigned style = window.state.resizable ? ResizableStyle : FixedStyle; + if(window.state.fullScreen) style = 0; RECT rc = { 0, 0, 640, 480 }; - AdjustWindowRect(&rc, window.state.resizable ? ResizableStyle : FixedStyle, window.state.menuVisible); + AdjustWindowRect(&rc, style, window.state.menuVisible); unsigned statusHeight = 0; if(window.state.statusVisible) { RECT src; diff --git a/bsnes/phoenix/windows/windows.hpp b/bsnes/phoenix/windows/windows.hpp index 308864c3..bd9fb565 100755 --- a/bsnes/phoenix/windows/windows.hpp +++ b/bsnes/phoenix/windows/windows.hpp @@ -160,7 +160,7 @@ struct pWidget : public pObject { void setEnabled(bool enabled); void setFocused(); void setFont(Font &font); - void setGeometry(const Geometry &geometry); + virtual void setGeometry(const Geometry &geometry); void setVisible(bool visible); pWidget(Widget &widget) : widget(widget) {} @@ -201,6 +201,7 @@ struct pComboBox : public pWidget { pComboBox(ComboBox &comboBox) : pWidget(comboBox), comboBox(comboBox) {} void constructor(); + void setGeometry(const Geometry &geometry); void setParent(Window &parent); }; @@ -273,6 +274,7 @@ struct pListView : public pWidget { pListView(ListView &listView) : pWidget(listView), listView(listView) {} void constructor(); + void setGeometry(const Geometry &geometry); void setParent(Window &parent); }; diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 44f2043f..a8233025 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,12 +1,12 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "075.14"; + static const char Version[] = "075.15"; static const unsigned SerializerVersion = 18; } } -#define DEBUGGER +//#define DEBUGGER #include