339 lines
12 KiB
C
339 lines
12 KiB
C
/*
|
|
PokeMini - Pokémon-Mini Emulator
|
|
Copyright (C) 2009-2015 JustBurn
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
#include <math.h>
|
|
#include "SGtkXDrawingView.h"
|
|
|
|
#include "Font8x12.h"
|
|
|
|
uint8_t *sgtkx_drawing_view_font = (uint8_t *)Font8x12;
|
|
|
|
void sgtkx_drawing_view_setfont(uint8_t *font)
|
|
{
|
|
if (font) sgtkx_drawing_view_font = font;
|
|
else sgtkx_drawing_view_font = (uint8_t *)Font8x12;
|
|
}
|
|
|
|
void sgtkx_drawing_view_drawhline(SGtkXDrawingView *widg, int y, int start, int end, uint32_t color)
|
|
{
|
|
if ((y < 0) || (y >= widg->height)) return;
|
|
if (start < 0) start = 0;
|
|
else if (start >= widg->width) return;
|
|
if (end < 0) return;
|
|
else if (end > widg->width) end = widg->width;
|
|
uint32_t *imgptr = (uint32_t *)widg->imgptr + y * widg->pitch + start;
|
|
while (start != end) {
|
|
*imgptr = color;
|
|
imgptr++;
|
|
start++;
|
|
}
|
|
}
|
|
|
|
void sgtkx_drawing_view_drawvline(SGtkXDrawingView *widg, int x, int start, int end, uint32_t color)
|
|
{
|
|
if ((x < 0) || (x >= widg->width)) return;
|
|
if (start < 0) start = 0;
|
|
else if (start >= widg->height) return;
|
|
if (end < 0) return;
|
|
else if (end > widg->height) end = widg->height;
|
|
uint32_t *imgptr = (uint32_t *)widg->imgptr + start * widg->pitch + x;
|
|
while (start != end) {
|
|
*imgptr = color;
|
|
imgptr += widg->pitch;
|
|
start++;
|
|
}
|
|
}
|
|
|
|
void sgtkx_drawing_view_drawsrect(SGtkXDrawingView *widg, int x, int y, int width, int height, uint32_t color)
|
|
{
|
|
sgtkx_drawing_view_drawhline(widg, y, x, x + width, color);
|
|
sgtkx_drawing_view_drawhline(widg, y + height - 1, x, x + width, color);
|
|
sgtkx_drawing_view_drawvline(widg, x, y, y + height, color);
|
|
sgtkx_drawing_view_drawvline(widg, x + width - 1, y, y + height, color);
|
|
}
|
|
|
|
void sgtkx_drawing_view_drawfrect(SGtkXDrawingView *widg, int x, int y, int width, int height, uint32_t color)
|
|
{
|
|
int z;
|
|
if ((x > widg->width) || (y > widg->height)) return;
|
|
if (x < 0) { width += x; x = 0; }
|
|
if (y < 0) { height += y; y = 0; }
|
|
if ((x + width) > widg->width) width = widg->width - x;
|
|
if ((y + height) > widg->height) height = widg->height - y;
|
|
if ((width <= 0) || (height <= 0)) return;
|
|
uint32_t *imgptr = (uint32_t *)widg->imgptr + y * widg->pitch + x;
|
|
while (height--) {
|
|
for (z=0; z<width; z++) imgptr[z] = color;
|
|
imgptr += widg->pitch;
|
|
}
|
|
}
|
|
|
|
void sgtkx_drawing_view_drawchar(SGtkXDrawingView *widg, int x, int y, uint32_t color, unsigned char ch)
|
|
{
|
|
unsigned char *chptr = (unsigned char *)sgtkx_drawing_view_font + (ch >> 4) * 1536 + (ch & 15) * 8;
|
|
int z, width = 8, height = 12;
|
|
if ((x > widg->width) || (y > widg->height)) return;
|
|
if (x < 0) { width += x; chptr += x; x = 0; }
|
|
if (y < 0) { height += y; chptr += y * 128; y = 0; }
|
|
if ((x + width) > widg->width) width = widg->width - x;
|
|
if ((y + height) > widg->height) height = widg->height - y;
|
|
if ((width <= 0) || (height <= 0)) return;
|
|
uint32_t *imgptr = (uint32_t *)widg->imgptr + y * widg->pitch + x;
|
|
while (height--) {
|
|
for (z=0; z<width; z++) if (chptr[z]) imgptr[z] = color;
|
|
imgptr += widg->pitch;
|
|
chptr += 128;
|
|
}
|
|
}
|
|
|
|
void sgtkx_drawing_view_drawtext(SGtkXDrawingView *widg, int x, int y, uint32_t color, char *format, ...)
|
|
{
|
|
char buffer[2048], ch;
|
|
int r, g, b;
|
|
char *sbuf;
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
vsprintf(buffer, format, args);
|
|
sbuf = buffer;
|
|
while ((ch = *sbuf++) != 0) {
|
|
if (ch == '\e') {
|
|
r = ((*sbuf++ - '0') * 28) & 255;
|
|
g = ((*sbuf++ - '0') * 28) & 255;
|
|
b = ((*sbuf++ - '0') * 28) & 255;
|
|
color = (r << 16) | (g << 8) | b;
|
|
continue;
|
|
}
|
|
sgtkx_drawing_view_drawchar(widg, x, y, color, ch);
|
|
x += 8.0;
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
void sgtkx_drawing_view_refresh(SGtkXDrawingView *widg)
|
|
{
|
|
gtk_widget_queue_draw(GTK_WIDGET(widg->da));
|
|
}
|
|
|
|
static void sgtkx_drawing_view_sbachanged(GtkAdjustment *adj, SGtkXDrawingView *widg)
|
|
{
|
|
int refresh = 1;
|
|
widg->sboffset = (int)adj->value;
|
|
if (widg->on_scroll) refresh = widg->on_scroll((void *)widg, (int)adj->value, (int)adj->lower, (int)adj->upper);
|
|
if (refresh) sgtkx_drawing_view_refresh(widg);
|
|
}
|
|
|
|
static void sgtkx_drawing_view_size_request(GtkWidget *widget, GdkRectangle *allocation, SGtkXDrawingView *widg)
|
|
{
|
|
if (widg->on_resize) widg->on_resize((void *)widg, (int)allocation->width, (int)allocation->height, 0);
|
|
}
|
|
|
|
static gboolean sgtkx_drawing_view_exposure(GtkWidget *widget, GdkEventExpose *event, SGtkXDrawingView *widg)
|
|
{
|
|
cairo_t *cr;
|
|
|
|
// Initialize cairo
|
|
cr = gdk_cairo_create(widget->window);
|
|
gdk_cairo_rectangle(cr, &event->area);
|
|
cairo_clip(cr);
|
|
|
|
// Recreate image cache on resize
|
|
if ((widget->allocation.width != widg->width) || (widget->allocation.height != widg->height)) {
|
|
cairo_surface_destroy(widg->surface);
|
|
widg->width = widget->allocation.width;
|
|
widg->height = widget->allocation.height;
|
|
if (widg->width < 0) widg->width = 8;
|
|
if (widg->height < 0) widg->height = 8;
|
|
widg->pitch = (widget->allocation.width + 7) & ~7;
|
|
widg->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, widg->pitch, widg->height);
|
|
widg->imgptr = (uint32_t *)cairo_image_surface_get_data(widg->surface);
|
|
if (widg->on_imgresize) widg->on_imgresize((void *)widg, widg->width, widg->height, widg->pitch);
|
|
}
|
|
|
|
// Callback exposure
|
|
if (widg->on_exposure) widg->on_exposure((void *)widg, widg->width, widg->height, widg->pitch);
|
|
|
|
// Paint image
|
|
cairo_set_source_surface(cr, widg->surface, 0, 0);
|
|
cairo_paint(cr);
|
|
|
|
// Destroy cairo
|
|
cairo_destroy (cr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean sgtkx_drawing_view_scroll_event(GtkWidget *widget, GdkEventScroll *event, SGtkXDrawingView *widg)
|
|
{
|
|
if (event->direction == GDK_SCROLL_UP) {
|
|
sgtkx_drawing_view_sbvalue(widg, (int)widg->sba->value - (int)widg->sba->page_size);
|
|
} else if (event->direction == GDK_SCROLL_DOWN) {
|
|
sgtkx_drawing_view_sbvalue(widg, (int)widg->sba->value + (int)widg->sba->page_size);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean sgtkx_drawing_view_button_event(GtkWidget *widget, GdkEventButton *event, SGtkXDrawingView *widg)
|
|
{
|
|
int refresh = 0;
|
|
if (event->type == GDK_BUTTON_PRESS) {
|
|
widg->buttons |= (1 << event->button);
|
|
if (widg->on_buttonpress) refresh = widg->on_buttonpress((void *)widg, event->button, 1, 0);
|
|
} else if (event->type == GDK_BUTTON_RELEASE) {
|
|
widg->buttons &= ~(1 << event->button);
|
|
if (widg->on_buttonrelease) refresh = widg->on_buttonrelease((void *)widg, event->button, 0, 0);
|
|
}
|
|
if (refresh) sgtkx_drawing_view_refresh(widg);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean sgtkx_drawing_view_motion_notify(GtkWidget *widget, GdkEventMotion *event, SGtkXDrawingView *widg)
|
|
{
|
|
int refresh = 0;
|
|
widg->mousex = (int)event->x;
|
|
widg->mousey = (int)event->y;
|
|
if (widg->on_motion) refresh = widg->on_motion((void *)widg, widg->mousex, widg->mousey, 0);
|
|
if (refresh) sgtkx_drawing_view_refresh(widg);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean sgtkx_drawing_view_enterleave_notify(GtkWidget *widget, GdkEvent *event, SGtkXDrawingView *widg)
|
|
{
|
|
int refresh = 0;
|
|
if (event->type == GDK_ENTER_NOTIFY) {
|
|
widg->mouseinside = 1;
|
|
if (widg->on_enterleave) refresh = widg->on_enterleave((void *)widg, 1, 0, 0);
|
|
} else if (event->type == GDK_LEAVE_NOTIFY) {
|
|
widg->mouseinside = 0;
|
|
if (widg->on_enterleave) refresh = widg->on_enterleave((void *)widg, 0, 0, 0);
|
|
}
|
|
if (refresh) sgtkx_drawing_view_refresh(widg);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int sgtkx_drawing_view_new(SGtkXDrawingView *widg, int scrollbar)
|
|
{
|
|
// Horizontal Box
|
|
widg->box = GTK_BOX(gtk_hbox_new(FALSE, 0));
|
|
if (!widg->box) return 0;
|
|
|
|
// Drawing area
|
|
widg->da = GTK_DRAWING_AREA(gtk_drawing_area_new());
|
|
if (!widg->da) return 0;
|
|
gtk_widget_set_double_buffered(GTK_WIDGET(widg->da), FALSE);
|
|
gtk_widget_add_events(GTK_WIDGET(widg->da), GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
|
|
gtk_widget_add_events(GTK_WIDGET(widg->da), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
|
|
g_signal_connect(GTK_WIDGET(widg->da), "size-allocate", G_CALLBACK(sgtkx_drawing_view_size_request), widg);
|
|
g_signal_connect(GTK_WIDGET(widg->da), "expose_event", G_CALLBACK(sgtkx_drawing_view_exposure), widg);
|
|
g_signal_connect(GTK_WIDGET(widg->da), "motion_notify_event", G_CALLBACK(sgtkx_drawing_view_motion_notify), widg);
|
|
g_signal_connect(GTK_WIDGET(widg->da), "button_press_event", G_CALLBACK(sgtkx_drawing_view_button_event), widg);
|
|
g_signal_connect(GTK_WIDGET(widg->da), "button_release_event", G_CALLBACK(sgtkx_drawing_view_button_event), widg);
|
|
g_signal_connect(GTK_WIDGET(widg->da), "scroll_event", G_CALLBACK(sgtkx_drawing_view_scroll_event), widg);
|
|
g_signal_connect(GTK_WIDGET(widg->da), "enter_notify_event", G_CALLBACK(sgtkx_drawing_view_enterleave_notify), widg);
|
|
g_signal_connect(GTK_WIDGET(widg->da), "leave_notify_event", G_CALLBACK(sgtkx_drawing_view_enterleave_notify), widg);
|
|
gtk_box_pack_start(widg->box, GTK_WIDGET(widg->da), TRUE, TRUE, 0);
|
|
gtk_widget_show(GTK_WIDGET(widg->da));
|
|
|
|
// Scroll bar adjustment
|
|
widg->sba = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 1.0, 16.0, 0.0));
|
|
g_signal_connect(widg->sba, "value_changed", G_CALLBACK(sgtkx_drawing_view_sbachanged), (gpointer)widg);
|
|
if (!widg->sba) return 0;
|
|
widg->sboffset = 0;
|
|
|
|
// Scroll bar
|
|
widg->sb = GTK_SCROLLBAR(gtk_vscrollbar_new(widg->sba));
|
|
if (!widg->sb) return 0;
|
|
gtk_box_pack_start(widg->box, GTK_WIDGET(widg->sb), FALSE, TRUE, 0);
|
|
if (scrollbar) gtk_widget_show(GTK_WIDGET(widg->sb));
|
|
|
|
// Surface
|
|
widg->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 8, 8);
|
|
widg->width = 8;
|
|
widg->height = 8;
|
|
widg->pitch = 8;
|
|
widg->imgptr = (uint32_t *)cairo_image_surface_get_data(widg->surface);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void sgtkx_drawing_view_sbvalue(SGtkXDrawingView *widg, int value)
|
|
{
|
|
double fullmax, fvalue;
|
|
fvalue = (double)value;
|
|
fullmax = widg->sba->upper - widg->sba->page_size;
|
|
if (fvalue < widg->sba->lower) {
|
|
widg->sboffset = widg->sba->lower;
|
|
gtk_adjustment_set_value(widg->sba, widg->sboffset);
|
|
} else if (fvalue > fullmax) {
|
|
widg->sboffset = fullmax;
|
|
gtk_adjustment_set_value(widg->sba, widg->sboffset);
|
|
} else {
|
|
widg->sboffset = fvalue;
|
|
gtk_adjustment_set_value(widg->sba, widg->sboffset);
|
|
}
|
|
}
|
|
|
|
void sgtkx_drawing_view_sbminmax(SGtkXDrawingView *widg, int min, int max)
|
|
{
|
|
double fullmax, fvalue;
|
|
gtk_adjustment_set_lower(widg->sba, (double)min);
|
|
gtk_adjustment_set_upper(widg->sba, (double)max);
|
|
fvalue = floor(widg->sba->value);
|
|
fullmax = widg->sba->upper - widg->sba->page_size;
|
|
if (fvalue < widg->sba->lower) {
|
|
gtk_adjustment_set_value(widg->sba, widg->sba->lower);
|
|
} else if (fvalue > fullmax) {
|
|
gtk_adjustment_set_value(widg->sba, fullmax);
|
|
}
|
|
}
|
|
|
|
void sgtkx_drawing_view_sbpage(SGtkXDrawingView *widg, int page_inc, int page_size)
|
|
{
|
|
double fullmax, fvalue;
|
|
gtk_adjustment_set_page_increment(widg->sba, (double)page_inc);
|
|
gtk_adjustment_set_page_size(widg->sba, (double)page_size);
|
|
fvalue = floor(widg->sba->value);
|
|
fullmax = widg->sba->upper - widg->sba->page_size;
|
|
if (fvalue < widg->sba->lower) {
|
|
gtk_adjustment_set_value(widg->sba, widg->sba->lower);
|
|
} else if (fvalue > fullmax) {
|
|
gtk_adjustment_set_value(widg->sba, fullmax);
|
|
}
|
|
}
|
|
|
|
static gboolean sgtkx_drawing_view_repaint_NOW(gpointer user_data)
|
|
{
|
|
sgtkx_drawing_view_refresh((SGtkXDrawingView *)user_data);
|
|
return FALSE;
|
|
}
|
|
|
|
void sgtkx_drawing_view_repaint_after(SGtkXDrawingView *widg, int milisec)
|
|
{
|
|
g_timeout_add(milisec, sgtkx_drawing_view_repaint_NOW, (gpointer)widg);
|
|
}
|