aseprite/src/core/app.cpp

721 lines
18 KiB
C++
Raw Normal View History

2007-11-16 18:25:45 +00:00
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2009 David Capello
2007-09-18 23:57:02 +00:00
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <assert.h>
2007-09-18 23:57:02 +00:00
#include <allegro.h>
/* #include <allegro/internal/aintern.h> */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "jinete/jinete.h"
#include "jinete/jintern.h"
2007-09-18 23:57:02 +00:00
#include "ase_exception.h"
#include "ui_context.h"
2007-09-23 20:13:58 +00:00
#include "commands/commands.h"
#include "console.h"
2007-09-18 23:57:02 +00:00
#include "core/app.h"
#include "core/cfg.h"
#include "core/core.h"
#include "core/drop_files.h"
#include "core/file_system.h"
2007-09-18 23:57:02 +00:00
#include "core/modules.h"
#include "dialogs/options.h"
#include "dialogs/tips.h"
#include "file/file.h"
#include "intl/intl.h"
#include "modules/editors.h"
#include "modules/gfx.h"
#include "modules/gui.h"
#include "modules/palettes.h"
2007-09-18 23:57:02 +00:00
#include "modules/recent.h"
#include "modules/rootmenu.h"
#include "modules/sprites.h"
#include "raster/image.h"
#include "raster/layer.h"
#include "raster/palette.h"
2007-09-18 23:57:02 +00:00
#include "raster/sprite.h"
#include "util/boundary.h"
2007-09-18 23:57:02 +00:00
#include "util/recscr.h"
#include "widgets/colbar.h"
#include "widgets/editor.h"
2007-09-23 20:13:58 +00:00
#include "widgets/menuitem.h"
2007-09-18 23:57:02 +00:00
#include "widgets/statebar.h"
#include "widgets/tabs.h"
2007-09-18 23:57:02 +00:00
#include "widgets/toolbar.h"
#ifdef ALLEGRO_WINDOWS
#include <winalleg.h>
#endif
2007-09-23 20:13:58 +00:00
/* options */
2007-09-18 23:57:02 +00:00
enum {
OPEN_GFX_FILE,
};
struct Option
2007-09-18 23:57:02 +00:00
{
int type;
char *data;
};
2007-09-18 23:57:02 +00:00
struct AppHook
{
void (*proc)(void *);
void *data;
AppHook(void (*proc)(void *), void *data) {
this->proc = proc;
this->data = data;
}
};
2007-09-18 23:57:02 +00:00
static char *exe_name; /* name of the program */
static JList apphooks[APP_EVENTS];
2007-09-18 23:57:02 +00:00
static JWidget top_window = NULL; /* top level window (the desktop) */
static JWidget box_menubar = NULL; /* box where the menu bar is */
static JWidget box_colorbar = NULL; /* box where the color bar is */
static JWidget box_toolbar = NULL; /* box where the tools bar is */
static JWidget box_statusbar = NULL; /* box where the status bar is */
static JWidget box_tabsbar = NULL; /* box where the tabs bar is */
static JWidget menubar = NULL; /* the menu bar widget */
static JWidget statusbar = NULL; /* the status bar widget */
static JWidget colorbar = NULL; /* the color bar widget */
static JWidget toolbar = NULL; /* the tool bar widget */
static JWidget tabsbar = NULL; /* the tabs bar widget */
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
static JList options; /* list of "Option" structures (options to execute) */
2007-09-18 23:57:02 +00:00
static char *palette_filename = NULL;
static void tabsbar_select_callback(JWidget tabs, void *data, int button);
static void check_args(int argc, char *argv[]);
2007-09-23 20:13:58 +00:00
static void usage(int status);
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
static Option *option_new(int type, const char *data);
static void option_free(Option *option);
2007-09-18 23:57:02 +00:00
/**
* Initializes the application loading the modules, setting the
* graphics mode, loading the configuration and resources, etc.
*/
Application::Application(int argc, char *argv[])
2007-09-18 23:57:02 +00:00
{
exe_name = argv[0];
/* initialize application hooks */
for (int c=0; c<APP_EVENTS; ++c)
apphooks[c] = NULL;
/* initialize language suppport */
2007-09-23 20:13:58 +00:00
intl_init();
2007-09-18 23:57:02 +00:00
/* install the `core' of ASE application */
core_init();
2007-09-18 23:57:02 +00:00
/* install the file-system access module */
file_system_init();
2007-09-18 23:57:02 +00:00
/* init configuration */
2007-09-23 20:13:58 +00:00
ase_config_init();
2007-09-18 23:57:02 +00:00
/* load the language file */
2007-09-23 20:13:58 +00:00
intl_load_lang();
2007-09-18 23:57:02 +00:00
/* search options in the arguments */
check_args(argc, argv);
2007-09-18 23:57:02 +00:00
/* GUI is the default mode */
if (!(ase_mode & MODE_BATCH))
ase_mode |= MODE_GUI;
/* install 'raster' stuff */
gfxobj_init();
2007-09-18 23:57:02 +00:00
/* install the modules */
modules_init(REQUIRE_INTERFACE);
2007-09-18 23:57:02 +00:00
/* custom default palette? */
if (palette_filename) {
Palette *pal;
2007-09-18 23:57:02 +00:00
PRINTF("Loading custom palette file: %s\n", palette_filename);
pal = palette_load(palette_filename);
if (pal == NULL)
throw ase_exception(std::string("Error loading default palette from: ")
+ palette_filename);
2007-09-18 23:57:02 +00:00
set_default_palette(pal);
palette_free(pal);
2007-09-18 23:57:02 +00:00
}
/* set system palette to the default one */
set_current_palette(NULL, TRUE);
2007-09-18 23:57:02 +00:00
}
/**
* Runs the ASE application. In GUI mode it's the top-level window, in
* console/scripting it just runs the specified scripts.
*/
void Application::run()
2007-09-18 23:57:02 +00:00
{
2007-09-23 20:13:58 +00:00
Option *option;
2007-09-18 23:57:02 +00:00
JLink link;
/* initialize GUI interface */
if (ase_mode & MODE_GUI) {
JWidget view, editor;
PRINTF("GUI mode\n");
/* setup the GUI screen */
jmouse_set_cursor(JI_CURSOR_NORMAL);
jmanager_refresh_screen();
2007-09-18 23:57:02 +00:00
/* load main window */
top_window = load_widget("main.jid", "main_window");
if (!top_window) {
allegro_message("Error loading data data/jids/main.jid file.\n"
"You have to reinstall the program.\n");
exit(1);
}
2007-09-18 23:57:02 +00:00
box_menubar = jwidget_find_name(top_window, "menubar");
box_editors = jwidget_find_name(top_window, "editor");
box_colorbar = jwidget_find_name(top_window, "colorbar");
box_toolbar = jwidget_find_name(top_window, "toolbar");
box_statusbar = jwidget_find_name(top_window, "statusbar");
box_tabsbar = jwidget_find_name(top_window, "tabsbar");
menubar = jmenubar_new();
statusbar = statusbar_new();
colorbar = colorbar_new(box_colorbar->align());
toolbar = toolbar_new();
tabsbar = tabs_new(tabsbar_select_callback);
view = editor_view_new();
editor = create_new_editor();
2007-09-18 23:57:02 +00:00
/* append the NULL sprite to the tabs */
tabs_append_tab(tabsbar, "Nothing", NULL);
2007-09-18 23:57:02 +00:00
/* configure all widgets to expansives */
jwidget_expansive(menubar, TRUE);
jwidget_expansive(statusbar, TRUE);
jwidget_expansive(colorbar, TRUE);
jwidget_expansive(toolbar, TRUE);
jwidget_expansive(tabsbar, TRUE);
jwidget_expansive(view, TRUE);
2007-09-18 23:57:02 +00:00
/* prepare the first editor */
jview_attach(view, editor);
2007-09-18 23:57:02 +00:00
/* setup the menus */
jmenubar_set_menu(menubar, get_root_menu());
2007-09-18 23:57:02 +00:00
/* start text of status bar */
app_default_statusbar_message();
2007-09-18 23:57:02 +00:00
/* add the widgets in the boxes */
if (box_menubar) jwidget_add_child(box_menubar, menubar);
if (box_editors) jwidget_add_child(box_editors, view);
if (box_colorbar) jwidget_add_child(box_colorbar, colorbar);
if (box_toolbar) jwidget_add_child(box_toolbar, toolbar);
if (box_statusbar) jwidget_add_child(box_statusbar, statusbar);
if (box_tabsbar) jwidget_add_child(box_tabsbar, tabsbar);
2007-09-18 23:57:02 +00:00
/* prepare the window */
jwindow_remap(top_window);
2007-09-18 23:57:02 +00:00
/* rebuild menus */
app_realloc_sprite_list();
app_realloc_recent_list();
2007-09-18 23:57:02 +00:00
/* set current editor */
set_current_editor(editor);
2007-09-18 23:57:02 +00:00
/* open the window */
jwindow_open(top_window);
2007-09-18 23:57:02 +00:00
/* refresh the screen */
jmanager_refresh_screen();
2007-09-18 23:57:02 +00:00
}
/* set background mode for non-GUI modes */
/* if (!(ase_mode & MODE_GUI)) */
/* set_display_switch_mode(SWITCH_BACKAMNESIA); */
set_display_switch_mode(SWITCH_BACKGROUND);
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
/* procress options */
PRINTF("Processing options...\n");
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
JI_LIST_FOR_EACH(options, link) {
2008-09-30 21:01:54 +00:00
option = reinterpret_cast<Option*>(link->data);
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
switch (option->type) {
2007-09-18 23:57:02 +00:00
case OPEN_GFX_FILE: {
/* load the sprite */
Sprite *sprite = sprite_load(option->data);
2007-09-18 23:57:02 +00:00
if (!sprite) {
/* error */
if (ase_mode & MODE_GUI)
2007-09-23 20:13:58 +00:00
jalert(_("Error<<Error loading file \"%s\"||&Close"), option->data);
2007-09-18 23:57:02 +00:00
else
2007-09-23 20:13:58 +00:00
user_printf(_("Error loading file \"%s\"\n"), option->data);
2007-09-18 23:57:02 +00:00
}
else {
/* mount and select the sprite */
UIContext* context = UIContext::instance();
context->add_sprite(sprite);
context->set_current_sprite(sprite);
2007-09-18 23:57:02 +00:00
if (ase_mode & MODE_GUI) {
/* show it */
set_sprite_in_more_reliable_editor(context->get_first_sprite());
2007-09-18 23:57:02 +00:00
/* recent file */
2007-09-23 20:13:58 +00:00
recent_file(option->data);
2007-09-18 23:57:02 +00:00
}
}
break;
}
}
2007-09-23 20:13:58 +00:00
option_free(option);
2007-09-18 23:57:02 +00:00
}
2007-09-23 20:13:58 +00:00
jlist_free(options);
2007-09-18 23:57:02 +00:00
/* just batch mode */
if (ase_mode & MODE_BATCH) {
PRINTF("Batch mode\n");
}
/* run the GUI */
else if (ase_mode & MODE_GUI) {
/* select language */
dialogs_select_language(FALSE);
2007-09-18 23:57:02 +00:00
// show tips only if there are not a current sprite
if (!UIContext::instance()->get_current_sprite())
dialogs_tips(FALSE);
2007-09-18 23:57:02 +00:00
// support to drop files from Windows explorer
install_drop_files();
gui_run();
2007-09-18 23:57:02 +00:00
uninstall_drop_files();
2007-09-18 23:57:02 +00:00
/* stop recording */
if (is_rec_screen())
rec_screen_off();
/* remove the root-menu from the menu-bar (because the rootmenu
module should destroy it) */
jmenubar_set_menu(menubar, NULL);
2007-09-18 23:57:02 +00:00
/* destroy the top-window */
jwidget_free(top_window);
top_window = NULL;
}
}
/**
* Finishes the ASE application.
*/
Application::~Application()
2007-09-18 23:57:02 +00:00
{
JLink link;
int c;
// remove ase handlers
2007-09-18 23:57:02 +00:00
PRINTF("Uninstalling ASE\n");
app_trigger_event(APP_EXIT);
// destroy application hooks
for (c=0; c<APP_EVENTS; ++c) {
if (apphooks[c] != NULL) {
JI_LIST_FOR_EACH(apphooks[c], link) {
AppHook* apphook = reinterpret_cast<AppHook*>(link->data);
delete apphook;
}
jlist_free(apphooks[c]);
apphooks[c] = NULL;
}
}
2007-09-18 23:57:02 +00:00
/* finalize modules, configuration and core */
modules_exit();
UIContext::destroy_instance();
editor_cursor_exit();
boundary_exit();
gfxobj_exit();
2007-09-18 23:57:02 +00:00
ase_config_exit();
file_system_exit();
2007-09-18 23:57:02 +00:00
core_exit();
intl_exit();
}
void app_add_hook(int app_event, void (*proc)(void *data), void *data)
{
assert(app_event >= 0 && app_event < APP_EVENTS);
if (apphooks[app_event] == NULL)
apphooks[app_event] = jlist_new();
jlist_append(apphooks[app_event], new AppHook(proc, data));
}
void app_trigger_event(int app_event)
{
assert(app_event >= 0 && app_event < APP_EVENTS);
if (apphooks[app_event] != NULL) {
JList list = apphooks[app_event];
JLink link;
JI_LIST_FOR_EACH(list, link) {
AppHook *h = (AppHook *)link->data;
(h->proc)(h->data);
}
}
}
/**
* Updates palette and redraw the screen.
*/
void app_refresh_screen(const Sprite* sprite)
2007-09-18 23:57:02 +00:00
{
assert(screen != NULL);
if (sprite)
set_current_palette(sprite_get_palette(sprite, sprite->frame), false);
else
set_current_palette(NULL, false);
2007-09-18 23:57:02 +00:00
// redraw the screen
jmanager_refresh_screen();
2007-09-18 23:57:02 +00:00
}
/**
* Regenerates the label for each tab in the @em tabsbar.
*/
void app_realloc_sprite_list()
2007-09-18 23:57:02 +00:00
{
UIContext* context = UIContext::instance();
const SpriteList& list = context->get_sprite_list();
/* insert all other sprites */
for (SpriteList::const_iterator
it = list.begin(); it != list.end(); ++it) {
Sprite* sprite = *it;
tabs_set_text_for_tab(tabsbar, get_filename(sprite->filename), sprite);
}
2007-09-18 23:57:02 +00:00
}
/**
* Updates the recent list menu.
*
* @warning This routine can't be used when a menu callback was
* called, because, it destroy the menus, you should use
* schedule_rebuild_recent_list() instead (src/modules/gui.c).
*/
bool app_realloc_recent_list()
2007-09-18 23:57:02 +00:00
{
JWidget list_menuitem = get_recent_list_menuitem();
JWidget menuitem;
/* update the recent file list menu item */
if (list_menuitem) {
Command *cmd_open_file;
2007-09-18 23:57:02 +00:00
JWidget submenu;
if (jmenuitem_has_submenu_opened(list_menuitem))
return FALSE;
cmd_open_file = command_get_by_name(CMD_OPEN_FILE);
submenu = jmenuitem_get_submenu(list_menuitem);
2007-09-18 23:57:02 +00:00
if (submenu) {
jmenuitem_set_submenu(list_menuitem, NULL);
jwidget_free(submenu);
2007-09-18 23:57:02 +00:00
}
2007-09-23 20:13:58 +00:00
submenu = jmenu_new();
jmenuitem_set_submenu(list_menuitem, submenu);
2007-09-18 23:57:02 +00:00
if (jlist_first(get_recent_files_list())) {
2007-09-23 20:13:58 +00:00
const char *filename;
2007-09-18 23:57:02 +00:00
JLink link;
JI_LIST_FOR_EACH(get_recent_files_list(), link) {
2008-09-30 21:01:54 +00:00
filename = reinterpret_cast<const char*>(link->data);
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
menuitem = menuitem_new(get_filename(filename),
cmd_open_file,
filename);
jwidget_add_child(submenu, menuitem);
2007-09-18 23:57:02 +00:00
}
}
else {
2007-09-23 20:13:58 +00:00
menuitem = menuitem_new(_("Nothing"), NULL, NULL);
jwidget_disable(menuitem);
jwidget_add_child(submenu, menuitem);
2007-09-18 23:57:02 +00:00
}
}
return TRUE;
2007-09-18 23:57:02 +00:00
}
int app_get_current_image_type()
2007-09-18 23:57:02 +00:00
{
Context* context = UIContext::instance();
assert(context != NULL);
Sprite* sprite = context->get_current_sprite();
if (sprite != NULL)
return sprite->imgtype;
else if (screen != NULL && bitmap_color_depth(screen) == 8)
2007-09-18 23:57:02 +00:00
return IMAGE_INDEXED;
else
return IMAGE_RGB;
2007-09-18 23:57:02 +00:00
}
JWidget app_get_top_window() { return top_window; }
JWidget app_get_menubar() { return menubar; }
JWidget app_get_statusbar() { return statusbar; }
JWidget app_get_colorbar() { return colorbar; }
JWidget app_get_toolbar() { return toolbar; }
JWidget app_get_tabsbar() { return tabsbar; }
void app_default_statusbar_message()
{
statusbar_set_text(app_get_statusbar(), 250,
"ASE " VERSION ", " COPYRIGHT);
}
2007-09-18 23:57:02 +00:00
int app_get_fg_color(Sprite *sprite)
{
color_t c = colorbar_get_fg_color(colorbar);
assert(sprite != NULL);
if (sprite->layer != NULL)
return get_color_for_layer(sprite->layer, c);
else
return get_color_for_image(sprite->imgtype, c);
}
int app_get_bg_color(Sprite *sprite)
{
color_t c = colorbar_get_bg_color(colorbar);
assert(sprite != NULL);
if (sprite->layer != NULL)
return get_color_for_layer(sprite->layer, c);
else
return get_color_for_image(sprite->imgtype, c);
}
int app_get_color_to_clear_layer(Layer *layer)
{
/* all transparent layers are cleared with the mask color */
color_t color = color_mask();
/* the `Background' is erased with the `Background Color' */
if (layer != NULL && layer_is_background(layer))
color = colorbar_get_bg_color(colorbar);
return get_color_for_layer(layer, color);
}
static void tabsbar_select_callback(JWidget tabs, void *data, int button)
2007-09-18 23:57:02 +00:00
{
// Note: data can be NULL (the "Nothing" tab)
Sprite* sprite = (Sprite*)data;
// put as current sprite
UIContext* context = UIContext::instance();
context->show_sprite(sprite);
// middle button: close the sprite
if (data && (button & 4))
command_execute(command_get_by_name(CMD_CLOSE_FILE), NULL);
2007-09-18 23:57:02 +00:00
}
/**
* Looks the inpunt arguments in the command line.
*/
static void check_args(int argc, char *argv[])
2007-09-18 23:57:02 +00:00
{
Console console;
2007-09-18 23:57:02 +00:00
int i, n, len;
char *arg;
2007-09-23 20:13:58 +00:00
options = jlist_new();
2007-09-18 23:57:02 +00:00
for (i=1; i<argc; i++) {
arg = argv[i];
for (n=0; arg[n] == '-'; n++);
len = strlen(arg+n);
2007-09-18 23:57:02 +00:00
/* option */
if ((n > 0) && (len > 0)) {
/* use other palette file */
if (strncmp(arg+n, "palette", len) == 0) {
2007-09-18 23:57:02 +00:00
if (++i < argc)
palette_filename = argv[i];
else
usage(1);
2007-09-18 23:57:02 +00:00
}
/* video resolution */
else if (strncmp(arg+n, "resolution", len) == 0) {
2007-09-18 23:57:02 +00:00
if (++i < argc) {
int c, num1=0, num2=0, num3=0;
char *tok;
/* el pr<70>ximo argumento debe indicar un formato de
resoluci<EFBFBD>n algo como esto: 320x240[x8] o [8] */
c = 0;
for (tok=ustrtok(argv[i], "x"); tok;
tok=ustrtok(NULL, "x")) {
2007-09-18 23:57:02 +00:00
switch (c) {
case 0: num1 = ustrtol(tok, NULL, 10); break;
case 1: num2 = ustrtol(tok, NULL, 10); break;
case 2: num3 = ustrtol(tok, NULL, 10); break;
2007-09-18 23:57:02 +00:00
}
c++;
}
switch (c) {
case 1:
set_config_int("GfxMode", "Depth", num1);
2007-09-18 23:57:02 +00:00
break;
case 2:
case 3:
set_config_int("GfxMode", "Width", num1);
set_config_int("GfxMode", "Height", num2);
2007-09-18 23:57:02 +00:00
if (c == 3)
set_config_int("GfxMode", "Depth", num3);
2007-09-18 23:57:02 +00:00
break;
}
}
else {
console.printf(_("%s: option \"res\" requires an argument\n"), exe_name);
2007-09-18 23:57:02 +00:00
usage(1);
}
}
/* verbose mode */
else if (strncmp(arg+n, "verbose", len) == 0) {
ase_mode |= MODE_VERBOSE;
}
/* show help */
else if (strncmp(arg+n, "help", len) == 0) {
usage(0);
}
/* show version */
else if (strncmp(arg+n, "version", len) == 0) {
ase_mode |= MODE_BATCH;
console.printf("ase %s\n", VERSION);
2007-09-18 23:57:02 +00:00
}
/* invalid argument */
else {
usage(1);
}
}
/* graphic file to open */
else if (n == 0)
2007-09-23 20:13:58 +00:00
jlist_append(options, option_new(OPEN_GFX_FILE, argv[i]));
2007-09-18 23:57:02 +00:00
}
}
/**
* Shows the available options for the program
*/
2007-09-18 23:57:02 +00:00
static void usage(int status)
{
Console console;
2007-09-18 23:57:02 +00:00
/* show options */
if (!status) {
/* copyright */
console.printf
2007-09-18 23:57:02 +00:00
("ase %s -- allegro-sprite-editor, %s\n"
COPYRIGHT "\n\n",
2007-09-18 23:57:02 +00:00
VERSION, _("The Ultimate Sprites Factory"));
/* usage */
console.printf
2007-09-18 23:57:02 +00:00
("%s\n %s [%s] [%s]...\n\n",
_("Usage:"), exe_name, _("OPTION"), _("FILE"));
/* options */
console.printf
2007-09-18 23:57:02 +00:00
("%s:\n"
" -palette GFX-FILE %s\n"
" -resolution WxH[xBPP] %s\n"
" -verbose %s\n"
" -help %s\n"
" -version %s\n"
"\n",
_("Options"),
_("Use a specific palette by default"),
2007-09-18 23:57:02 +00:00
_("Change the resolution to use"),
_("Explain what is being done (in stderr or a log file)"),
_("Display this help and exits"),
_("Output version information and exit"));
/* web-site */
console.printf
2007-09-18 23:57:02 +00:00
("%s: %s\n%s\n\n %s\n\n",
_("Find more information in the ASE's official web site at:"), WEBSITE);
}
/* how to show options */
else {
console.printf(_("Try \"%s --help\" for more information.\n"), exe_name);
2007-09-18 23:57:02 +00:00
}
2007-09-23 20:13:58 +00:00
exit(status);
2007-09-18 23:57:02 +00:00
}
2007-09-23 20:13:58 +00:00
static Option *option_new(int type, const char *data)
2007-09-18 23:57:02 +00:00
{
Option *option = new Option;
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
option->type = type;
option->data = jstrdup(data);
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
return option;
2007-09-18 23:57:02 +00:00
}
static void option_free(Option* option)
2007-09-18 23:57:02 +00:00
{
2007-09-23 20:13:58 +00:00
jfree(option->data);
delete option;
}