/*
* An embeddable "mines" component.
*
* This looks extremely useless, but so do you :-)
*
* Author:
* Michael Meeks <michael@imaginator.com>
*
*/
#include <config.h>
#include <bonobo.h>
#include "minefield.h"
/* Include the face */
#include "face-sad.xpm"
#include "face-smile.xpm"
#include "face-win.xpm"
#include "face-cool.xpm"
#include "face-worried.xpm"
/*
* BonoboControl data
*/
typedef struct {
BonoboControl *bonobo_object;
BonoboUIComponent *uic;
MineField *data;
GtkMineFieldView *mfield;
} control_data_t;
/*
* This callback is invoked when the BonoboControl object
* encounters a fatal CORBA exception.
*/
static void
control_system_exception_cb (BonoboControl *control, CORBA_Object corba_object,
CORBA_Environment *ev, gpointer data)
{
bonobo_object_unref (BONOBO_OBJECT (control));
}
/*
* This function updates all of an control's views to reflect the
* image data stored in the control.
*/
static void
control_update (control_data_t *control_data)
{
gtk_widget_queue_draw (GTK_WIDGET (control_data->mfield));
}
static void
load_board (BonoboPersistStream *ps,
const Bonobo_Stream stream,
Bonobo_Persist_ContentType type,
void *closure,
CORBA_Environment *ev)
{
control_data_t *control_data = closure;
MineField *mf;
Bonobo_Stream_iobuf *buffer;
char *str;
int bx, by, j;
g_return_if_fail (control_data != NULL);
g_return_if_fail (control_data->data != NULL);
if (*type && g_strcasecmp (type, "application/x-mines") != 0) {
CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
ex_Bonobo_Persist_WrongDataType, NULL);
return;
}
mf = control_data->data;
bonobo_stream_client_read_string (stream, &str, ev);
if (ev->_major != CORBA_NO_EXCEPTION || str == NULL) {
CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
ex_Bonobo_Persist_WrongDataType, NULL);
return;
}
sscanf (str, "%2u%2u\n", &bx, &by);
g_free (str);
if (bx > 128 || by > 128) {
CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
ex_Bonobo_Persist_WrongDataType, NULL);
return;
}
minefield_set_size (mf, bx, by);
minefield_restart (mf);
for (j = 0; j < by; j++) {
int i;
Bonobo_Stream_read (stream, bx * 2 + 1, &buffer, ev);
if (ev->_major != CORBA_NO_EXCEPTION)
return;
else if (buffer->_length != bx * 2 + 1) {
CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
ex_Bonobo_Persist_WrongDataType,
NULL);
return;
}
for (i = 0; i < bx; i++)
minefield_set_at (mf, i, j, buffer->_buffer [(i << 1)],
buffer->_buffer [(i << 1) + 1]);
CORBA_free (buffer);
}
control_update (control_data);
}
static void
save_board (BonoboPersistStream *ps,
const Bonobo_Stream stream,
Bonobo_Persist_ContentType type,
void *closure,
CORBA_Environment *ev)
{
control_data_t *control_data = closure;
MineField *mf;
char *data;
int j;
g_return_if_fail (control_data != NULL);
g_return_if_fail (control_data->data != NULL);
if (*type && g_strcasecmp (type, "application/x-mines") != 0) {
CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
ex_Bonobo_Persist_WrongDataType, NULL);
return;
}
mf = control_data->data;
bonobo_stream_client_printf (stream, TRUE, ev, "%2u%2u\n",
mf->xsize, mf->ysize);
if (ev->_major != CORBA_NO_EXCEPTION)
return;
data = g_malloc (mf->xsize * 2 + 1);
for (j = 0; j < mf->ysize; j++) {
int i;
for (i = 0; i < mf->xsize; i++)
minefield_get_at (mf, i, j, &data [(i << 1)],
&data [(i << 1) + 1]);
data [(i << 1)] = '\n';
bonobo_stream_client_write (stream, data, mf->xsize * 2 + 1, ev);
if (ev->_major != CORBA_NO_EXCEPTION)
return;
}
g_free (data);
}
static Bonobo_Persist_ContentTypeList *
content_types (BonoboPersistStream *ps, void *closure, CORBA_Environment *ev)
{
return bonobo_persist_generate_content_types (1, "application/x-mines");
}
static void
verb_NewGame_cb (BonoboUIComponent *uic, gpointer user_data, const char *cname)
{
control_data_t *control_data = (control_data_t *) user_data;
GdkPixbuf *pixbuf;
/* Start a new game: Happy face. */
pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) face_smile_xpm);
bonobo_ui_util_set_pixbuf (uic, "/commands/NewGame", pixbuf);
gdk_pixbuf_unref (pixbuf);
minefield_restart (control_data->data);
control_update (control_data);
}
/*
* When one of our controls is activated, we merge our menus
* in with our container's menus.
*/
static void
control_create_menus (control_data_t *control_data)
{
BonoboControl *control = control_data->bonobo_object;
Bonobo_UIContainer remote_uic;
GdkPixbuf *pixbuf;
static char ui [] =
"<Root>"
" <commands>"
" <cmd name=\"NewGame\" _label=\"New game\" _tip=\"Start a new game\"/>"
" <cmd name=\"OpenGame\" _label=\"Open game\" _tip=\"Load a saved game\"/>"
" </commands>"
" <menu>"
" <submenu name=\"Game\" _label=\"_Game\">"
" <menuitem name=\"NewGame\" verb=\"\"/>"
" <menuitem name=\"OpenGame\" verb=\"\"/>"
" </submenu>"
" </menu>"
" <dockitem name=\"Game\">"
" <toolitem name=\"NewGame\" verb=\"\"/>"
" </dockitem>"
"</Root>";
/*
* Get our container's UIContainer server.
*/
remote_uic = bonobo_control_get_remote_ui_container (control);
/*
* We have to deal gracefully with containers
* which don't have a UIContainer running.
*/
if (remote_uic == CORBA_OBJECT_NIL) {
g_warning ("No UI container!");
return;
}
/*
* Give our BonoboUIComponent object a reference to the
* container's UIContainer server.
*/
bonobo_ui_component_set_container (control_data->uic, remote_uic);
/*
* Unref the UI container we have been passed.
*/
bonobo_object_release_unref (remote_uic, NULL);
/* Set up the UI from the XML string. */
{
BonoboUINode *node;
node = bonobo_ui_node_from_string (ui);
bonobo_ui_util_translate_ui (node);
bonobo_ui_util_fixup_help (control_data->uic, node,
DATADIR, "gnomines");
bonobo_ui_component_set_tree (control_data->uic, "/", node, NULL);
bonobo_ui_node_free (node);
}
/* Start a new game: Happy face. */
pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) face_smile_xpm);
bonobo_ui_util_set_pixbuf (control_data->uic, "/commands/NewGame", pixbuf);
gdk_pixbuf_unref (pixbuf);
}
static void
control_remove_menus (control_data_t *control_data)
{
bonobo_ui_component_unset_container (control_data->uic);
}
/*
* Clean up our supplementary BonoboControl data sturctures.
*/
static void
control_destroy_cb (BonoboControl *control, gpointer data)
{
control_data_t *control_data = (control_data_t *) data;
g_message ("control_destroy_cb");
if (control_data->data)
minefield_destroy (control_data->data);
control_data->data = NULL;
g_free (control_data);
}
static void
control_activate_cb (BonoboControl *control, gboolean activate, gpointer data)
{
control_data_t *control_data = (control_data_t *) data;
/*
* The ControlFrame has just asked the Control (that's us) to be
* activated or deactivated. We must reply to the ControlFrame
* and say whether or not we want our activation state to
* change. We are an acquiescent BonoboControl, so we just agree
* with whatever the ControlFrame told us. Most components
* should behave this way.
*/
bonobo_control_activate_notify (control, activate);
/*
* If we were just activated, we merge in our menu entries.
* If we were just deactivated, we remove them.
*/
if (activate)
control_create_menus (control_data);
else
control_remove_menus (control_data);
}
static void
control_set_frame_cb (BonoboControl *control, gpointer data)
{
control_create_menus ((control_data_t *) data);
}
static void
update_control (GtkWidget *widget, control_data_t *control_data)
{
control_update (control_data);
}
static void
game_over_cb (GtkWidget *widget, control_data_t *control_data)
{
BonoboControl *control = control_data->bonobo_object;
BonoboUIComponent *uic;
GdkPixbuf *pixbuf;
uic = bonobo_control_get_ui_component (control);
/* Dead: Sad face in toolbar */
pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) face_sad_xpm);
bonobo_ui_util_set_pixbuf (uic, "/commands/NewGame", pixbuf);
gdk_pixbuf_unref (pixbuf);
}
static void
win_cb (GtkWidget *widget, control_data_t *control_data)
{
BonoboControl *control = control_data->bonobo_object;
BonoboUIComponent *uic;
GdkPixbuf *pixbuf;
uic = bonobo_control_get_ui_component (control);
/* Win: Happy face */
pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) face_win_xpm);
bonobo_ui_util_set_pixbuf (uic, "/commands/NewGame", pixbuf);
gdk_pixbuf_unref (pixbuf);
}
static void
look_cb (GtkWidget *widget, control_data_t *control_data)
{
BonoboControl *control = control_data->bonobo_object;
BonoboUIComponent *uic;
GdkPixbuf *pixbuf;
uic = bonobo_control_get_ui_component (control);
/* Look: Puzzled face */
pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) face_worried_xpm);
bonobo_ui_util_set_pixbuf (uic, "/commands/NewGame", pixbuf);
gdk_pixbuf_unref (pixbuf);
}
static void
unlook_cb (GtkWidget *widget, control_data_t *control_data)
{
BonoboControl *control = control_data->bonobo_object;
BonoboUIComponent *uic;
GdkPixbuf *pixbuf;
uic = bonobo_control_get_ui_component (control);
/* Unlooking means the game has started: Cool face */
pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) face_cool_xpm);
bonobo_ui_util_set_pixbuf (uic, "/commands/NewGame", pixbuf);
gdk_pixbuf_unref (pixbuf);
}
static BonoboObject *
bonobo_mines_factory (BonoboGenericFactory *this, void *data)
{
BonoboControl *bonobo_object;
control_data_t *control_data;
BonoboPersistStream *stream;
GtkWidget *vbox;
/*
* Create a data structure in which we can store
* Control-object-specific data about this document.
*/
control_data = g_new0 (control_data_t, 1);
if (control_data == NULL)
return NULL;
control_data->data = minefield_new ();
minefield_set_size (control_data->data, 16, 16);
minefield_set_mines (control_data->data, 40);
minefield_restart (control_data->data);
vbox = gtk_vbox_new (TRUE, 0);
control_data->mfield = (GtkMineFieldView *)gtk_minefield_new_view (control_data->data);
/* Second set of signals is to control the face in the toolbar */
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "marks_changed",
GTK_SIGNAL_FUNC (update_control), control_data);
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "explode",
GTK_SIGNAL_FUNC (update_control), control_data);
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "explode",
GTK_SIGNAL_FUNC (game_over_cb), control_data);
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "win",
GTK_SIGNAL_FUNC (update_control), control_data);
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "win",
GTK_SIGNAL_FUNC (win_cb), control_data);
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "look",
GTK_SIGNAL_FUNC (update_control), control_data);
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "look",
GTK_SIGNAL_FUNC (look_cb), control_data);
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "unlook",
GTK_SIGNAL_FUNC (update_control), control_data);
gtk_signal_connect(GTK_OBJECT (control_data->mfield), "unlook",
GTK_SIGNAL_FUNC (unlook_cb), control_data);
gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (control_data->mfield),
TRUE, TRUE, 0);
gtk_widget_show_all (vbox);
/*
* Create the BonoboControl object.
*/
bonobo_object = bonobo_control_new (vbox);
if (bonobo_object == NULL) {
gtk_widget_destroy (vbox);
minefield_destroy (control_data->data);
g_free (control_data);
return NULL;
}
control_data->bonobo_object = bonobo_object;
control_data->uic = bonobo_control_get_ui_component (bonobo_object);
/*
* When our container wants to activate this component, we will get
* the "activate" signal.
*/
gtk_signal_connect (GTK_OBJECT (bonobo_object), "activate",
GTK_SIGNAL_FUNC (control_activate_cb), control_data);
gtk_signal_connect (GTK_OBJECT (bonobo_object), "set_frame",
GTK_SIGNAL_FUNC (control_set_frame_cb), control_data);
/*
* The "system_exception" signal is raised when the BonoboControl
* encounters a fatal CORBA exception.
*/
gtk_signal_connect (GTK_OBJECT (bonobo_object), "system_exception",
GTK_SIGNAL_FUNC (control_system_exception_cb), control_data);
/*
* We'll need to be able to cleanup when this control gets
* destroyed.
*/
gtk_signal_connect (GTK_OBJECT (bonobo_object), "destroy",
GTK_SIGNAL_FUNC (control_destroy_cb), control_data);
/*
* Create the PersistStream object.
*/
stream = bonobo_persist_stream_new (load_board, save_board,
NULL, content_types,
control_data);
if (stream == NULL) {
bonobo_object_unref (BONOBO_OBJECT (bonobo_object));
gtk_widget_destroy (vbox);
minefield_destroy (control_data->data);
g_free (control_data);
return NULL;
}
bonobo_object_add_interface (BONOBO_OBJECT (bonobo_object),
BONOBO_OBJECT (stream));
/*
* Add some verbs to the control.
*
* The container application will then have the programmatic
* ability to execute the verbs on the component. It will
* also provide a simple mechanism whereby the user can
* right-click on the component to create a popup menu
* listing the available verbs.
*
* We provide one simple verb whose job it is to clear the
* window.
*/
control_data->uic = bonobo_control_get_ui_component (bonobo_object);
bonobo_ui_component_add_verb (control_data->uic, "NewGame",
verb_NewGame_cb, control_data);
return BONOBO_OBJECT (bonobo_object);
}
BONOBO_OAF_FACTORY ("OAFIID:Bonobo_Sample_Mines_Factory",
"application-x-mines", VERSION,
bonobo_mines_factory,
NULL)
syntax highlighted by Code2HTML, v. 0.9.1