home *** CD-ROM | disk | FTP | other *** search
-
- /* gadget.c */
-
- #include <stdlib.h>
- #include "OS:wimp.h"
- #include "OS:wimpspriteop.h"
- #include "OS:window.h"
- #include "OS:button.h"
- #include "OS:gadget.h"
- #include "Dreamscape:x.h"
-
- #include "iconbar.h"
- #include "debug.h"
- #include "options.h"
- #include "gadget.h"
- #include "various.h"
- #include "font.h"
-
-
- /* The iconbar_gadget structure's contents are all private. */
-
- struct iconbar_gadget {
- /* Information given to us. */
- toolbox_o window;
- os_box bbox;
- char *text, /* 0 for none. */
- *task_name; /* 0 for none. */
- sprite_details sprite;
- /* If true, the sprite name has been replaced by its small version. */
- unsigned sprite_small: 1;
-
- /* Handlers. */
- iconbar_gadget_resize_handler *resize_handler;
- void *resize_handle;
-
- /* Cached info. */
- os_coord sprite_size;
- int text_width;
-
- /* Information about implementation. */
- unsigned showing: 1;
- int border_cmp, sprite_icon, text_icon;
- char *text_valid;
- font_object *font;
- };
-
-
- /* Internal function prototypes. */
-
- #define MODULE_NAME "iconbar_gadget"
-
- static bool read_width(iconbar_gadget *obj);
- static void read_sprite_info(iconbar_gadget *obj);
- static void read_text_info(iconbar_gadget *obj);
-
- static void create(iconbar_gadget *obj);
- static void create_border(iconbar_gadget *obj);
- static void create_sprite(iconbar_gadget *obj);
- static void create_text(iconbar_gadget *obj);
-
- static void destroy(iconbar_gadget *obj);
- static void destroy_border(iconbar_gadget *obj);
- static void destroy_sprite(iconbar_gadget *obj);
- static void destroy_text(iconbar_gadget *obj);
-
- static void redraw_contents(iconbar_gadget *obj);
- static void find_small_sprite(iconbar_gadget *obj);
-
-
- /* Creation and destruction. */
-
- /* Create a default iconbar_gadget. May return 0. */
- iconbar_gadget *iconbar_gadget_create(void)
- {
- iconbar_gadget *obj;
- LOGFUNC(create);
-
- /* Create structure. */
- obj = malloc(sizeof(iconbar_gadget));
- if(!obj) return 0;
-
- /* Zero fields. */
- obj->window = 0;
- obj->text = 0;
- obj->task_name = 0;
- obj->sprite.id.name = 0;
- obj->sprite.area = 0;
- obj->sprite.is_ptr = 0;
- obj->sprite_small = 0;
-
- obj->resize_handler = 0;
-
- obj->sprite_size.x = obj->sprite_size.y = obj->text_width = 0;
-
- obj->showing = 0;
- obj->border_cmp = obj->sprite_icon = obj->text_icon = -1;
- obj->text_valid = 0;
-
- /* Open font. */
- obj->font = font_open();
- if(!obj->font) { free(obj); return 0; }
-
- return obj;
- }
-
- void iconbar_gadget_destroy(iconbar_gadget *obj)
- {
- LOGFUNC(destroy);
- if(!obj) return;
-
- destroy(obj);
- free(obj->text);
- free(obj->task_name);
- if(!obj->sprite.is_ptr) free(obj->sprite.id.name);
- free(obj->sprite.area);
-
- font_close(obj->font);
-
- free(obj);
- }
-
-
- /* Methods for setting variables. */
-
- void iconbar_gadget_set_window(iconbar_gadget *obj, toolbox_o w)
- {
- LOGFUNC(set_window);
- if(!obj) return;
-
- if(w) {
- obj->window = w;
- create(obj);
- }
- else {
- destroy(obj);
- obj->window = 0;
- }
- }
-
- toolbox_o iconbar_gadget_get_window(const iconbar_gadget *obj)
- {
- return obj->window;
- }
-
- void iconbar_gadget_set_bbox(iconbar_gadget *obj, const os_box *b)
- {
- LOGFUNC(set_bbox);
- if(!obj) return;
- obj->bbox = *b;
- if(obj->showing) create(obj);
- }
-
- void iconbar_gadget_get_bbox(const iconbar_gadget *obj, os_box *b)
- {
- LOGFUNC(get_bbox);
- if(!obj) return;
- *b = obj->bbox;
- }
-
- void iconbar_gadget_set_resize_handler(iconbar_gadget *obj,
- iconbar_gadget_resize_handler *function, void *handle)
- {
- LOGFUNC(set_resize_handler);
- if(!obj) return;
- obj->resize_handler = function;
- obj->resize_handle = handle;
- }
-
- void iconbar_gadget_set_text(iconbar_gadget *obj, const char *text)
- {
- LOGFUNC(set_text);
- if(!obj) return;
-
- /* This next line is a gross hack. Sorry. */
- if(!text && obj->task_name && options->use_task_name) return;
-
- /* Update if new text is different. */
- if(!obj->text || !text || strcmp(obj->text, text)) {
- /* Clear out old info. */
- destroy_text(obj); /* Icon uses pointers to old data. */
- free(obj->text);
-
- /* Copy new info. */
- obj->text = my_strdup(text);
- if(!obj->text && text) x_throw_message(x_msg_memory());
-
- /* Update as necessary. */
- read_text_info(obj);
- if(read_width(obj))
- { if(obj->showing) create(obj); }
- else { if(obj->showing) { create_text(obj); redraw_contents(obj); } }
- }
- }
-
- /* Set the sprite details. Sprite name (if given) is copied, but we must
- free the sprite area when finished with. */
- void iconbar_gadget_set_sprite(iconbar_gadget *obj,
- const sprite_details *sprite)
- {
- LOGFUNC(set_sprite);
- if(!obj) return;
-
- /* Don't bother checking whether the sprite name has changed, because the
- underlying sprite might still have been modified. */
-
- /* Clear out old info. */
- destroy_sprite(obj); /* Icon uses pointers to old data. */
- if(!obj->sprite.is_ptr) free(obj->sprite.id.name);
- free(obj->sprite.area);
-
- /* Copy new info. */
- if(sprite->is_ptr)
- obj->sprite.id.ptr = sprite->id.ptr;
- else {
- obj->sprite.id.name = my_strdup(sprite->id.name);
- if(!obj->sprite.id.name && sprite->id.name)
- x_throw_message(x_msg_memory());
- }
- obj->sprite.area = sprite->area;
- obj->sprite.is_ptr = sprite->is_ptr;
-
- /* Update as necessary. */
- find_small_sprite(obj);
- read_sprite_info(obj);
- if(read_width(obj))
- { if(obj->showing) create(obj); }
- else { if(obj->showing) { create_sprite(obj); redraw_contents(obj); } }
- }
-
- void iconbar_gadget_set_task_name(iconbar_gadget *obj, const char *name)
- {
- LOGFUNC(set_task_name);
-
- if(options->use_task_name) {
- free(obj->task_name);
- obj->task_name = my_strdup(name);
- if(!obj->task_name && name) x_throw_message(x_msg_memory());
- }
-
- /* This is a gross hack. Sorry. */
- if(!obj->text && name && options->use_task_name)
- iconbar_gadget_set_text(obj, name);
- }
-
-
- /* Internal functions. */
-
- /* Returns true if the width has changed. Before returning, will call the
- resize handler, if necessary, to find the new bounding box. Will not
- read the new sprite info. */
- static bool read_width(iconbar_gadget *obj)
- {
- int new_width;
- LOGFUNC(read_width);
-
- /* This depends on how text and sprite are to be arranged. */
- if(options->arrangement == arrangement_TEXT_BOTTOM) {
- /* Text appears underneath sprite. */
- int swidth = obj->sprite_size.x + options->sprite_margin * 2,
- twidth = obj->text_width + options->text_margin * 2;
- new_width = twidth > swidth ? twidth : swidth;
- }
- else {
- /* Text appears to the right of the sprite. */
- new_width = options->icon_h_margins * 2 + obj->sprite_size.x +
- (obj->text ? obj->text_width + options->sprite_text_gap : 0);
- }
- /* Round width to nearest pixel. */
- {
- int xeig;
- os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR, &xeig);
- new_width = (new_width + (1 << xeig) - 1) &~ ((1 << xeig) - 1);
- }
-
- DEBUGF1("Overall width is %i\n", new_width);
-
- if(new_width != (obj->bbox.x1 - obj->bbox.x0) ||
- options->icon_height != (obj->bbox.y1 - obj->bbox.y0)) {
- os_coord new_size;
- new_size.x = new_width;
- new_size.y = options->icon_height;
-
- if(obj->resize_handler) {
- if(obj->resize_handler(obj, &obj->bbox, &new_size,
- obj->resize_handle)) x_throw_message("Error when resizing icon");
- }
- else {
- obj->bbox.x1 = obj->bbox.x0 + new_size.x;
- obj->bbox.y0 = obj->bbox.y1 - new_size.y;
- }
- return 1; /* Width changed. */
- }
- return 0; /* No change. */
- }
-
- static void read_sprite_info(iconbar_gadget *obj)
- {
- os_mode sprite_mode;
- os_error *fail;
- int xeig, yeig;
- LOGFUNC(read_sprite_info);
-
- /* Do we look at the Wimp sprite pool? */
- if(!obj->sprite.area) {
- fail = xwimpspriteop_read_sprite_size(obj->sprite.id.name,
- &obj->sprite_size.x, &obj->sprite_size.y, 0, &sprite_mode);
- }
- else {
- /* Or do we use own own, copied sprite area? */
- if(!obj->sprite.is_ptr) {
- fail = xosspriteop_read_sprite_size(osspriteop_NAME,
- obj->sprite.area, (osspriteop_id) obj->sprite.id.name,
- &obj->sprite_size.x, &obj->sprite_size.y, 0, &sprite_mode);
- }
- else {
- fail = xosspriteop_read_sprite_size(osspriteop_PTR,
- obj->sprite.area, obj->sprite.id.ptr,
- &obj->sprite_size.x, &obj->sprite_size.y, 0, &sprite_mode);
- }
- }
-
- /* If the sprite was broken and the SWI failed, drop back to
- sensible default sizes */
- if(fail) {
- obj->sprite_size.x = obj->sprite_size.y = 68;
- DEBUGF2("Reading sprite size failed; using defaults (%i by %i)\n",
- obj->sprite_size.x, obj->sprite_size.y);
- }
- else {
- /* And then convert pixels to OS units */
- os_read_mode_variable(sprite_mode, os_MODEVAR_XEIG_FACTOR, &xeig);
- os_read_mode_variable(sprite_mode, os_MODEVAR_YEIG_FACTOR, &yeig);
- obj->sprite_size.x <<= xeig;
- obj->sprite_size.y <<= yeig;
- DEBUGF2("Sprite size is %i by %i\n",
- obj->sprite_size.x, obj->sprite_size.y);
- }
-
- /* Take account of half size sprites option. */
- if(options->small_icons && !obj->sprite_small) {
- obj->sprite_size.x /= 2;
- obj->sprite_size.y /= 2;
- }
- }
-
- static void read_text_info(iconbar_gadget *obj)
- {
- LOGFUNC(read_text_info);
- obj->text_width = obj->text ?
- font_text_width(obj->font, obj->text) : 0;
- DEBUGF1("Text width is %i\n", obj->text_width);
- }
-
- static void create(iconbar_gadget *obj)
- {
- LOGFUNC(create);
- create_border(obj);
- create_sprite(obj);
- create_text(obj);
- obj->showing = 1;
- }
-
- static void button_template(gadget_object **gadget, button_gadget **button)
- {
- *gadget = malloc(sizeof(gadget_object) + sizeof(button_gadget));
- *button = (button_gadget *) (*gadget)->gadget;
- if(!*gadget) x_throw_message(x_msg_memory());
-
- (*gadget)->flags = button_ALLOW_MENU_CLICKS;
- (*gadget)->class_no = class_BUTTON;
- (*gadget)->size = 6*4 + 5*4;
- (*gadget)->cmp = -1;
- (*gadget)->help_message = 0;
- (*gadget)->help_limit = 0;
- }
-
- static void create_border(iconbar_gadget *obj)
- {
- gadget_object *gadget;
- button_gadget *button;
- LOGFUNC(create_border);
-
- destroy_border(obj);
- button_template(&gadget, &button);
-
- /* Create border component. */
- gadget->bbox = obj->bbox;
- button->flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
- (options->icon_borders != border_type_NONE ? wimp_ICON_BORDER : 0) |
- (options->icon_bg_colour != -1 ? wimp_ICON_FILLED : 0) |
- wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
- (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT) |
- (options->text_colour << wimp_ICON_FG_COLOUR_SHIFT) |
- ((options->icon_bg_colour != -1 ? options->icon_bg_colour :
- options->window_bg_colour) << wimp_ICON_BG_COLOUR_SHIFT);
- button->value = "";
- button->value_limit = 1;
- /* The border appearance is configurable. */
- switch(options->icon_borders) {
- case border_type_SLAB_OUT: button->validation = "r1"; break;
- case border_type_SLAB_IN: button->validation = "r2"; break;
- case border_type_NONE:
- case border_type_PLAIN:
- default: button->validation = 0; break;
- }
- button->validation_limit = 4;
- obj->border_cmp = window_add_gadget(0, obj->window, gadget);
- free(gadget);
- }
-
- static void create_sprite(iconbar_gadget *obj)
- {
- /* Create a single-component, sprite icon as a Wimp icon. */
- /* We have to do this because the Toolbox won't let us create Button
- gadgets with sprites in different pools. D'oh! */
- wimp_icon_create i;
- LOGFUNC(create_sprite);
- destroy_sprite(obj);
-
- i.w = window_get_wimp_handle(0, obj->window);
- /* Work out bounding box. */
- /* This depends on where the text is to be placed. */
- if(options->arrangement == arrangement_TEXT_BOTTOM) {
- /* Text goes at the bottom, so the sprite goes just above. If the sprite
- is big enough that it would overlap the top, it is allowed to overlap
- the text instead. */
- i.icon.extent.x0 = obj->bbox.x0 + options->border_size;
- i.icon.extent.x1 = obj->bbox.x1 - options->border_size;
- if(obj->text) {
- int top;
- i.icon.extent.y0 = obj->bbox.y0 +
- options->border_size + options->text_height;
- i.icon.extent.y1 = i.icon.extent.y0 + obj->sprite_size.y;
- top = obj->bbox.y1 - options->border_size;
- if(i.icon.extent.y1 > top) {
- i.icon.extent.y1 = top;
- i.icon.extent.y0 = top - obj->sprite_size.y;
- }
- }
- else {
- i.icon.extent.y0 = obj->bbox.y0 + options->border_size;
- i.icon.extent.y1 = obj->bbox.y1 - options->border_size;
- }
- }
- else {
- /* Text goes to the right of the sprite. The sprite's position is fixed
- to the left of the bounding box, and centred vertically. */
- i.icon.extent.x0 = obj->bbox.x0 + options->icon_h_margins;
- i.icon.extent.x1 = i.icon.extent.x0 + obj->sprite_size.x;
- i.icon.extent.y0 = obj->bbox.y0 + options->border_size;
- i.icon.extent.y1 = obj->bbox.y1 - options->border_size;
- }
- /* Icon flags. */
- i.icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
- wimp_ICON_VCENTRED | wimp_ICON_HCENTRED |
- (options->small_icons && !obj->sprite_small ?
- wimp_ICON_HALF_SIZE : 0) |
- (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT) |
- (options->text_colour << wimp_ICON_FG_COLOUR_SHIFT) |
- ((options->icon_bg_colour != -1 ? options->icon_bg_colour :
- options->window_bg_colour) << wimp_ICON_BG_COLOUR_SHIFT);
- /* Icon data. */
- i.icon.data.indirected_sprite.id = obj->sprite.is_ptr ?
- obj->sprite.id.ptr : (osspriteop_id) obj->sprite.id.name;
- i.icon.data.indirected_sprite.area = (obj->sprite.area ?
- obj->sprite.area : wimpspriteop_AREA);
- i.icon.data.indirected_sprite.size = obj->sprite.is_ptr ? 0 : 12;
- /* Create the icon. */
- obj->sprite_icon = wimp_create_icon(&i);
- }
-
- static void create_text(iconbar_gadget *obj)
- {
- LOGFUNC(create_text);
- destroy_text(obj);
-
- if(obj->text) {
- int font_handle;
- wimp_icon_create i;
- i.w = window_get_wimp_handle(0, obj->window);
-
- /* Bounding box. */
- /* This depends on how text and sprite are to be arranged. */
- if(options->arrangement == arrangement_TEXT_BOTTOM) {
- /* Text position is fixed at the bottom of the bounding box. */
- i.icon.extent.x0 = obj->bbox.x0 + options->border_size;
- i.icon.extent.x1 = obj->bbox.x1 - options->border_size;
- i.icon.extent.y0 = obj->bbox.y0 + options->border_size;
- i.icon.extent.y1 = i.icon.extent.y0 + options->text_height;
- }
- else {
- /* Text position is just right of the sprite, and centred
- vertically. */
- /* Allow options->icon_h_margins extra space either side of the text
- box, because the Wimp tries to align the text within its box, and
- we don't want it to do that. */
- i.icon.extent.x0 = obj->bbox.x0 + obj->sprite_size.x +
- options->sprite_text_gap;
- i.icon.extent.x1 = i.icon.extent.x0 + obj->text_width +
- options->icon_h_margins * 2;
- i.icon.extent.y0 = obj->bbox.y0 + options->border_size;
- i.icon.extent.y1 = obj->bbox.y1 - options->border_size;
- }
- /* Flags. */
- font_handle = font_get_handle(obj->font);
- i.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
- (options->arrangement == arrangement_TEXT_BOTTOM ?
- wimp_ICON_HCENTRED : 0) |
- wimp_ICON_VCENTRED |
- (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT) |
- (font_handle ?
- (font_handle << wimp_ICON_FONT_HANDLE_SHIFT) |
- wimp_ICON_ANTI_ALIASED :
- (options->text_colour << wimp_ICON_FG_COLOUR_SHIFT) |
- ((options->icon_bg_colour != -1 ? options->icon_bg_colour :
- options->window_bg_colour) << wimp_ICON_BG_COLOUR_SHIFT));
- /* Data. */
- i.icon.data.indirected_text.text = obj->text;
- i.icon.data.indirected_text.size = strlen(obj->text) + 1;
- /* Use validation string for font colours. */
- if(font_handle) {
- char *v = obj->text_valid = malloc(4);
- if(!v) x_throw_message(x_msg_memory());
- v[0] = 'f';
- #define HEX_DIGIT(n) \
- ((n) >= 10 ? 'a' + (n) - 10 : \
- '0' + (n))
- v[1] = HEX_DIGIT(options->icon_bg_colour != -1 ?
- options->icon_bg_colour : options->window_bg_colour);
- v[2] = HEX_DIGIT(options->text_colour);
- #undef HEX_DIGIT
- v[3] = 0;
- i.icon.data.indirected_text.validation = v;
- }
- else i.icon.data.indirected_text.validation = 0;
- /* Create it. */
- obj->text_icon = wimp_create_icon(&i);
- }
- }
-
- static void destroy(iconbar_gadget *obj)
- {
- LOGFUNC(destroy);
- if(!obj->showing) return;
-
- destroy_border(obj);
- destroy_sprite(obj);
- destroy_text(obj);
- obj->showing = 0;
- }
-
- static void destroy_border(iconbar_gadget *obj)
- {
- LOGFUNC(destroy_border);
- if(obj->border_cmp == -1) return;
- window_remove_gadget(0, obj->window, obj->border_cmp);
- obj->border_cmp = -1;
- }
-
- static void destroy_sprite(iconbar_gadget *obj)
- {
- LOGFUNC(destroy_sprite);
- if(obj->sprite_icon == -1) return;
- wimp_delete_icon(window_get_wimp_handle(0, obj->window), obj->sprite_icon);
- obj->sprite_icon = -1;
- }
-
- static void destroy_text(iconbar_gadget *obj)
- {
- LOGFUNC(destroy_text);
- if(obj->text_icon == -1) return;
- wimp_delete_icon(window_get_wimp_handle(0, obj->window), obj->text_icon);
- obj->text_icon = -1;
-
- free(obj->text_valid);
- obj->text_valid = 0;
- }
-
- /* Redraws the contents of an icon (text and sprite), but not border, to
- avoid flicker. */
- static void redraw_contents(iconbar_gadget *obj)
- {
- wimp_draw draw;
- bool more;
- LOGFUNC(redraw_contents);
-
- draw.w = window_get_wimp_handle(0, obj->window);
- draw.box.x0 = obj->bbox.x0 + options->border_size;
- draw.box.y0 = obj->bbox.y0 + options->border_size;
- draw.box.x1 = obj->bbox.x1 - options->border_size;
- draw.box.y1 = obj->bbox.y1 - options->border_size;
-
- more = wimp_update_window(&draw);
- while(more) more = wimp_get_rectangle(&draw);
- }
-
- /* If the small sprites option is set, this function looks for a small
- version of the existing sprite, and if one exists, makes a note of
- this. */
- static void find_small_sprite(iconbar_gadget *obj)
- {
- LOGFUNC(find_small_sprite);
- if(options->small_icons && !obj->sprite.area && !obj->sprite.is_ptr) {
- /* Look up sprite `smfoo' where `foo' is current sprite. */
- int current_len = strlen(obj->sprite.id.name);
- char *spr = malloc(2 + current_len + 1);
- if(!spr) x_throw_message(x_msg_memory());
- spr[0] = 's';
- spr[1] = 'm';
- memcpy(spr + 2, obj->sprite.id.name, current_len + 1);
- /* SWI should give error if sprite does not exist. */
- if(!xwimpspriteop_read_sprite_size(spr, 0, 0, 0, 0)) {
- /* Old sprite name is replaced, and flag set. */
- free(obj->sprite.id.name);
- obj->sprite.id.name = spr;
- obj->sprite_small = 1;
- }
- else {
- free(spr);
- obj->sprite_small = 0;
- }
- }
- else obj->sprite_small = 0;
- }
-
-
- /* Useful functions. */
-
- /* Duplicates a string. Can be passed 0, may return 0. */
- char *my_strdup(const char *s)
- {
- int len;
- char *n;
- if(!s) return 0;
-
- len = strlen(s);
- n = malloc(len + 1);
- if(!n) return 0;
- memcpy(n, s, len + 1);
- return n;
- }
-