mirror of https://github.com/bsnes-emu/bsnes.git
403 lines
14 KiB
C++
403 lines
14 KiB
C++
namespace phoenix {
|
|
|
|
static gint Window_close(GtkWidget* widget, GdkEvent* event, Window* window) {
|
|
if(window->onClose) window->onClose();
|
|
else window->setVisible(false);
|
|
if(window->state.modal && !window->visible()) window->setModal(false);
|
|
return true;
|
|
}
|
|
|
|
static gboolean Window_expose(GtkWidget* widget, GdkEvent* event, Window* window) {
|
|
if(window->state.backgroundColorOverride == false) return false;
|
|
cairo_t* context = gdk_cairo_create(widget->window);
|
|
|
|
Color color = window->backgroundColor();
|
|
double red = (double)color.red / 255.0;
|
|
double green = (double)color.green / 255.0;
|
|
double blue = (double)color.blue / 255.0;
|
|
double alpha = (double)color.alpha / 255.0;
|
|
|
|
if(gdk_screen_is_composited(gdk_screen_get_default())
|
|
&& gdk_screen_get_rgba_colormap(gdk_screen_get_default())
|
|
) {
|
|
cairo_set_source_rgba(context, red, green, blue, alpha);
|
|
} else {
|
|
cairo_set_source_rgb(context, red, green, blue);
|
|
}
|
|
|
|
cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
|
|
cairo_paint(context);
|
|
cairo_destroy(context);
|
|
|
|
return false;
|
|
}
|
|
|
|
static gboolean Window_configure(GtkWidget* widget, GdkEvent* event, Window* window) {
|
|
if(gtk_widget_get_realized(window->p.widget) == false) return false;
|
|
if(window->visible() == false) return false;
|
|
GdkWindow *gdkWindow = gtk_widget_get_window(widget);
|
|
|
|
GdkRectangle border, client;
|
|
gdk_window_get_frame_extents(gdkWindow, &border);
|
|
gdk_window_get_geometry(gdkWindow, nullptr, nullptr, &client.width, &client.height, nullptr);
|
|
gdk_window_get_origin(gdkWindow, &client.x, &client.y);
|
|
|
|
if(window->state.fullScreen == false) {
|
|
//update geometry settings
|
|
settings->geometry.frameX = client.x - border.x;
|
|
settings->geometry.frameY = client.y - border.y;
|
|
settings->geometry.frameWidth = border.width - client.width;
|
|
settings->geometry.frameHeight = border.height - client.height;
|
|
if(window->state.backgroundColorOverride == false) {
|
|
GdkColor color = widget->style->bg[GTK_STATE_NORMAL];
|
|
settings->window.backgroundColor
|
|
= ((uint8_t)(color.red >> 8) << 16)
|
|
+ ((uint8_t)(color.green >> 8) << 8)
|
|
+ ((uint8_t)(color.blue >> 8) << 0);
|
|
}
|
|
settings->save();
|
|
}
|
|
|
|
Geometry geometry = {
|
|
client.x,
|
|
client.y + window->p.menuHeight(),
|
|
client.width,
|
|
client.height - window->p.menuHeight() - window->p.statusHeight()
|
|
};
|
|
|
|
//move
|
|
if(geometry.x != window->state.geometry.x || geometry.y != window->state.geometry.y) {
|
|
if(window->state.fullScreen == false) {
|
|
window->state.geometry.x = geometry.x;
|
|
window->state.geometry.y = geometry.y;
|
|
}
|
|
if(window->p.locked == false && window->onMove) window->onMove();
|
|
}
|
|
|
|
//size
|
|
if(geometry.width != window->state.geometry.width || geometry.height != window->state.geometry.height) {
|
|
window->p.onSizePending = true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void Window_drop(GtkWidget* widget, GdkDragContext* context, gint x, gint y,
|
|
GtkSelectionData* data, guint type, guint timestamp, Window* window) {
|
|
if(window->state.droppable == false) return;
|
|
lstring paths = DropPaths(data);
|
|
if(paths.empty()) return;
|
|
if(window->onDrop) window->onDrop(paths);
|
|
}
|
|
|
|
static gboolean Window_keyPress(GtkWidget* widget, GdkEventKey* event, Window* window) {
|
|
Keyboard::Keycode key = Keysym(event->keyval);
|
|
if(key != Keyboard::Keycode::None && window->onKeyPress) window->onKeyPress(key);
|
|
return false;
|
|
}
|
|
|
|
static gboolean Window_keyRelease(GtkWidget* widget, GdkEventKey* event, Window* window) {
|
|
Keyboard::Keycode key = Keysym(event->keyval);
|
|
if(key != Keyboard::Keycode::None && window->onKeyRelease) window->onKeyRelease(key);
|
|
return false;
|
|
}
|
|
|
|
static void Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, Window* window) {
|
|
//size-allocate sent from gtk_fixed_move(); detect if layout unchanged and return
|
|
if(allocation->width == window->p.lastAllocation.width
|
|
&& allocation->height == window->p.lastAllocation.height) return;
|
|
|
|
window->state.geometry.width = allocation->width;
|
|
window->state.geometry.height = allocation->height;
|
|
|
|
for(auto& layout : window->state.layout) {
|
|
Geometry geometry = window->geometry();
|
|
geometry.x = geometry.y = 0;
|
|
layout.setGeometry(geometry);
|
|
}
|
|
|
|
if(window->p.onSizePending && window->p.locked == false && window->onSize) {
|
|
window->p.onSizePending = false;
|
|
window->onSize();
|
|
}
|
|
|
|
window->p.lastAllocation = *allocation;
|
|
}
|
|
|
|
static void Window_sizeRequest(GtkWidget* widget, GtkRequisition* requisition, Window* window) {
|
|
requisition->width = window->state.geometry.width;
|
|
requisition->height = window->state.geometry.height;
|
|
}
|
|
|
|
Window& pWindow::none() {
|
|
static Window* window = nullptr;
|
|
if(window == nullptr) window = new Window;
|
|
return *window;
|
|
}
|
|
|
|
void pWindow::append(Layout& layout) {
|
|
Geometry geometry = this->geometry();
|
|
geometry.x = geometry.y = 0;
|
|
layout.setGeometry(geometry);
|
|
}
|
|
|
|
void pWindow::append(Menu& menu) {
|
|
if(window.state.menuFont) menu.p.setFont(window.state.menuFont);
|
|
else menu.p.setFont(Font::sans(8));
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(this->menu), menu.p.widget);
|
|
gtk_widget_show(menu.p.widget);
|
|
}
|
|
|
|
void pWindow::append(Widget& widget) {
|
|
if(widget.font().empty() && !window.state.widgetFont.empty()) {
|
|
widget.setFont(window.state.widgetFont);
|
|
}
|
|
|
|
if(GetParentWidget(&widget)) {
|
|
widget.p.gtkParent = GetParentWidget(&widget)->p.container(widget);
|
|
} else {
|
|
widget.p.gtkParent = formContainer;
|
|
}
|
|
|
|
gtk_fixed_put(GTK_FIXED(widget.p.gtkParent), widget.p.gtkWidget, 0, 0);
|
|
if(widget.state.font) widget.p.setFont(widget.state.font);
|
|
else if(window.state.widgetFont) widget.p.setFont(window.state.widgetFont);
|
|
else widget.p.setFont(Font::sans(8));
|
|
widget.setVisible(widget.visible());
|
|
}
|
|
|
|
Geometry pWindow::frameMargin() {
|
|
if(window.state.fullScreen) return {
|
|
0,
|
|
menuHeight(),
|
|
0,
|
|
menuHeight() + statusHeight()
|
|
};
|
|
|
|
return {
|
|
settings->geometry.frameX,
|
|
settings->geometry.frameY + menuHeight(),
|
|
settings->geometry.frameWidth,
|
|
settings->geometry.frameHeight + menuHeight() + statusHeight()
|
|
};
|
|
}
|
|
|
|
bool pWindow::focused() {
|
|
return gtk_window_is_active(GTK_WINDOW(widget));
|
|
}
|
|
|
|
Geometry pWindow::geometry() {
|
|
if(window.state.fullScreen) {
|
|
int x, y, width, height;
|
|
gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
|
|
gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
|
|
return {x, y + menuHeight(), width, height - menuHeight() - statusHeight()};
|
|
}
|
|
return window.state.geometry;
|
|
}
|
|
|
|
void pWindow::remove(Layout& layout) {
|
|
}
|
|
|
|
void pWindow::remove(Menu& menu) {
|
|
menu.p.orphan();
|
|
}
|
|
|
|
void pWindow::remove(Widget& widget) {
|
|
widget.p.orphan();
|
|
}
|
|
|
|
void pWindow::setBackgroundColor(Color color) {
|
|
GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
|
|
gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gdkColor);
|
|
}
|
|
|
|
void pWindow::setDroppable(bool droppable) {
|
|
gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
|
|
if(droppable) gtk_drag_dest_add_uri_targets(widget);
|
|
}
|
|
|
|
void pWindow::setFocused() {
|
|
gtk_window_present(GTK_WINDOW(widget));
|
|
}
|
|
|
|
void pWindow::setFullScreen(bool fullScreen) {
|
|
if(fullScreen == false) {
|
|
gtk_window_unfullscreen(GTK_WINDOW(widget));
|
|
} else {
|
|
gtk_window_fullscreen(GTK_WINDOW(widget));
|
|
/*unsigned monitor = gdk_screen_get_monitor_at_window(gdk_screen_get_default(), gtk_widget_get_window(widget));
|
|
GdkRectangle rectangle = {0};
|
|
gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor, &rectangle);
|
|
gtk_window_set_decorated(GTK_WINDOW(widget), false);
|
|
gtk_window_move(GTK_WINDOW(widget), rectangle.x, rectangle.y);
|
|
gtk_window_resize(GTK_WINDOW(widget), rectangle.width, rectangle.height);
|
|
gtk_widget_set_size_request(formContainer, rectangle.width, rectangle.height);*/
|
|
}
|
|
}
|
|
|
|
void pWindow::setGeometry(Geometry geometry) {
|
|
Geometry margin = frameMargin();
|
|
gtk_window_move(GTK_WINDOW(widget), geometry.x - margin.x, geometry.y - margin.y);
|
|
|
|
GdkGeometry geom;
|
|
geom.min_width = window.state.resizable ? 1 : window.state.geometry.width;
|
|
geom.min_height = window.state.resizable ? 1 : window.state.geometry.height;
|
|
gtk_window_set_geometry_hints(GTK_WINDOW(widget), GTK_WIDGET(widget), &geom, GDK_HINT_MIN_SIZE);
|
|
|
|
//gtk_window_set_policy(GTK_WINDOW(widget), true, true, false);
|
|
gtk_widget_set_size_request(formContainer, geometry.width, geometry.height);
|
|
gtk_window_resize(GTK_WINDOW(widget), geometry.width, geometry.height + menuHeight() + statusHeight());
|
|
|
|
for(auto& layout : window.state.layout) {
|
|
Geometry layoutGeometry = geometry;
|
|
layoutGeometry.x = layoutGeometry.y = 0;
|
|
layout.setGeometry(layoutGeometry);
|
|
}
|
|
}
|
|
|
|
void pWindow::setMenuFont(string font) {
|
|
for(auto& item : window.state.menu) item.p.setFont(font);
|
|
}
|
|
|
|
void pWindow::setMenuVisible(bool visible) {
|
|
gtk_widget_set_visible(menu, visible);
|
|
}
|
|
|
|
void pWindow::setModal(bool modal) {
|
|
if(modal == true) {
|
|
gtk_window_set_modal(GTK_WINDOW(widget), true);
|
|
while(window.state.modal) {
|
|
Application::processEvents();
|
|
usleep(20 * 1000);
|
|
}
|
|
gtk_window_set_modal(GTK_WINDOW(widget), false);
|
|
}
|
|
}
|
|
|
|
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(string font) {
|
|
pFont::setFont(status, font);
|
|
}
|
|
|
|
void pWindow::setStatusText(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(string text) {
|
|
gtk_window_set_title(GTK_WINDOW(widget), text);
|
|
}
|
|
|
|
void pWindow::setVisible(bool visible) {
|
|
gtk_widget_set_visible(widget, visible);
|
|
if(visible) {
|
|
if(gtk_widget_get_visible(menu)) {
|
|
GtkAllocation allocation;
|
|
gtk_widget_get_allocation(menu, &allocation);
|
|
settings->geometry.menuHeight = allocation.height;
|
|
}
|
|
|
|
if(gtk_widget_get_visible(status)) {
|
|
GtkAllocation allocation;
|
|
gtk_widget_get_allocation(status, &allocation);
|
|
settings->geometry.statusHeight = allocation.height;
|
|
}
|
|
}
|
|
}
|
|
|
|
void pWindow::setWidgetFont(string font) {
|
|
}
|
|
|
|
void pWindow::constructor() {
|
|
lastAllocation.width = 0;
|
|
lastAllocation.height = 0;
|
|
onSizePending = false;
|
|
|
|
widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
|
|
//if program was given a name, try and set the window taskbar icon from one of the pixmaps folders
|
|
if(applicationState.name.empty() == false) {
|
|
string filename = {"/usr/share/pixmaps/", applicationState.name, ".png"};
|
|
if(!file::exists(filename)) filename = {"/usr/local/share/pixmaps/", applicationState.name, ".png"};
|
|
if(file::exists(filename)) {
|
|
//maximum image size supported by GTK+ is 256x256; so we must scale larger images ourselves
|
|
nall::image icon(filename);
|
|
icon.scale(min(256u, icon.width), min(256u, icon.height), Interpolation::Hermite);
|
|
GdkPixbuf* pixbuf = CreatePixbuf(icon);
|
|
gtk_window_set_icon(GTK_WINDOW(widget), pixbuf);
|
|
g_object_unref(G_OBJECT(pixbuf));
|
|
}
|
|
}
|
|
|
|
GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
|
|
if(!colormap) colormap = gdk_screen_get_rgb_colormap(gdk_screen_get_default());
|
|
if(colormap) gtk_widget_set_colormap(widget, colormap);
|
|
|
|
gtk_window_set_resizable(GTK_WINDOW(widget), true);
|
|
#if GTK_MAJOR_VERSION >= 3
|
|
gtk_window_set_has_resize_grip(GTK_WINDOW(widget), false);
|
|
#endif
|
|
|
|
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("");
|
|
setResizable(window.state.resizable);
|
|
setGeometry(window.state.geometry);
|
|
setMenuFont(Font::sans(8));
|
|
setStatusFont(Font::sans(8));
|
|
|
|
g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window);
|
|
g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(Window_expose), (gpointer)&window);
|
|
g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window);
|
|
g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_drop), (gpointer)&window);
|
|
g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPress), (gpointer)&window);
|
|
g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyPress), (gpointer)&window);
|
|
|
|
g_signal_connect(G_OBJECT(formContainer), "size-allocate", G_CALLBACK(Window_sizeAllocate), (gpointer)&window);
|
|
g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)&window);
|
|
|
|
window.state.backgroundColor = Color(
|
|
(uint8_t)(settings->window.backgroundColor >> 16),
|
|
(uint8_t)(settings->window.backgroundColor >> 8),
|
|
(uint8_t)(settings->window.backgroundColor >> 0)
|
|
);
|
|
}
|
|
|
|
unsigned pWindow::menuHeight() {
|
|
return window.state.menuVisible ? settings->geometry.menuHeight : 0;
|
|
}
|
|
|
|
unsigned pWindow::statusHeight() {
|
|
return window.state.statusVisible ? settings->geometry.statusHeight : 0;
|
|
}
|
|
|
|
}
|