home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / programs / desktop / newbar / Source / NewBar / c / gadget < prev    next >
Text File  |  1998-08-06  |  19KB  |  642 lines

  1.  
  2. /* gadget.c */
  3.  
  4. #include <stdlib.h>
  5. #include "OS:wimp.h"
  6. #include "OS:wimpspriteop.h"
  7. #include "OS:window.h"
  8. #include "OS:button.h"
  9. #include "OS:gadget.h"
  10. #include "Dreamscape:x.h"
  11.  
  12. #include "iconbar.h"
  13. #include "debug.h"
  14. #include "options.h"
  15. #include "gadget.h"
  16. #include "various.h"
  17. #include "font.h"
  18.  
  19.  
  20. /* The iconbar_gadget structure's contents are all private. */
  21.  
  22. struct iconbar_gadget {
  23.   /* Information given to us. */
  24.   toolbox_o window;
  25.   os_box bbox;
  26.   char *text,        /* 0 for none. */
  27.        *task_name;    /* 0 for none. */
  28.   sprite_details sprite;
  29.   /* If true, the sprite name has been replaced by its small version. */
  30.   unsigned sprite_small: 1;
  31.  
  32.   /* Handlers. */
  33.   iconbar_gadget_resize_handler *resize_handler;
  34.   void *resize_handle;
  35.  
  36.   /* Cached info. */
  37.   os_coord sprite_size;
  38.   int text_width;
  39.  
  40.   /* Information about implementation. */
  41.   unsigned showing: 1;
  42.   int border_cmp, sprite_icon, text_icon;
  43.   char *text_valid;
  44.   font_object *font;
  45. };
  46.  
  47.  
  48. /* Internal function prototypes. */
  49.  
  50. #define MODULE_NAME    "iconbar_gadget"
  51.  
  52. static bool read_width(iconbar_gadget *obj);
  53. static void read_sprite_info(iconbar_gadget *obj);
  54. static void read_text_info(iconbar_gadget *obj);
  55.  
  56. static void create(iconbar_gadget *obj);
  57. static void create_border(iconbar_gadget *obj);
  58. static void create_sprite(iconbar_gadget *obj);
  59. static void create_text(iconbar_gadget *obj);
  60.  
  61. static void destroy(iconbar_gadget *obj);
  62. static void destroy_border(iconbar_gadget *obj);
  63. static void destroy_sprite(iconbar_gadget *obj);
  64. static void destroy_text(iconbar_gadget *obj);
  65.  
  66. static void redraw_contents(iconbar_gadget *obj);
  67. static void find_small_sprite(iconbar_gadget *obj);
  68.  
  69.  
  70. /* Creation and destruction. */
  71.  
  72. /* Create a default iconbar_gadget.  May return 0. */
  73. iconbar_gadget *iconbar_gadget_create(void)
  74. {
  75.   iconbar_gadget *obj;
  76.   LOGFUNC(create);
  77.  
  78.   /* Create structure. */
  79.   obj = malloc(sizeof(iconbar_gadget));
  80.   if(!obj) return 0;
  81.  
  82.   /* Zero fields. */
  83.   obj->window = 0;
  84.   obj->text = 0;
  85.   obj->task_name = 0;
  86.   obj->sprite.id.name = 0;
  87.   obj->sprite.area = 0;
  88.   obj->sprite.is_ptr = 0;
  89.   obj->sprite_small = 0;
  90.  
  91.   obj->resize_handler = 0;
  92.  
  93.   obj->sprite_size.x = obj->sprite_size.y = obj->text_width = 0;
  94.  
  95.   obj->showing = 0;
  96.   obj->border_cmp = obj->sprite_icon = obj->text_icon = -1;
  97.   obj->text_valid = 0;
  98.  
  99.   /* Open font. */
  100.   obj->font = font_open();
  101.   if(!obj->font) { free(obj); return 0; }
  102.  
  103.   return obj;
  104. }
  105.  
  106. void iconbar_gadget_destroy(iconbar_gadget *obj)
  107. {
  108.   LOGFUNC(destroy);
  109.   if(!obj) return;
  110.  
  111.   destroy(obj);
  112.   free(obj->text);
  113.   free(obj->task_name);
  114.   if(!obj->sprite.is_ptr) free(obj->sprite.id.name);
  115.   free(obj->sprite.area);
  116.  
  117.   font_close(obj->font);
  118.  
  119.   free(obj);
  120. }
  121.  
  122.  
  123. /* Methods for setting variables. */
  124.  
  125. void iconbar_gadget_set_window(iconbar_gadget *obj, toolbox_o w)
  126. {
  127.   LOGFUNC(set_window);
  128.   if(!obj) return;
  129.  
  130.   if(w) {
  131.     obj->window = w;
  132.     create(obj);
  133.   }
  134.   else {
  135.     destroy(obj);
  136.     obj->window = 0;
  137.   }
  138. }
  139.  
  140. toolbox_o iconbar_gadget_get_window(const iconbar_gadget *obj)
  141. {
  142.   return obj->window;
  143. }
  144.  
  145. void iconbar_gadget_set_bbox(iconbar_gadget *obj, const os_box *b)
  146. {
  147.   LOGFUNC(set_bbox);
  148.   if(!obj) return;
  149.   obj->bbox = *b;
  150.   if(obj->showing) create(obj);
  151. }
  152.  
  153. void iconbar_gadget_get_bbox(const iconbar_gadget *obj, os_box *b)
  154. {
  155.   LOGFUNC(get_bbox);
  156.   if(!obj) return;
  157.   *b = obj->bbox;
  158. }
  159.  
  160. void iconbar_gadget_set_resize_handler(iconbar_gadget *obj,
  161.     iconbar_gadget_resize_handler *function, void *handle)
  162. {
  163.   LOGFUNC(set_resize_handler);
  164.   if(!obj) return;
  165.   obj->resize_handler = function;
  166.   obj->resize_handle = handle;
  167. }
  168.  
  169. void iconbar_gadget_set_text(iconbar_gadget *obj, const char *text)
  170. {
  171.   LOGFUNC(set_text);
  172.   if(!obj) return;
  173.  
  174.   /* This next line is a gross hack.  Sorry. */
  175.   if(!text && obj->task_name && options->use_task_name) return;
  176.  
  177.   /* Update if new text is different. */
  178.   if(!obj->text || !text || strcmp(obj->text, text)) {
  179.     /* Clear out old info. */
  180.     destroy_text(obj); /* Icon uses pointers to old data. */
  181.     free(obj->text);
  182.  
  183.     /* Copy new info. */
  184.     obj->text = my_strdup(text);
  185.     if(!obj->text && text) x_throw_message(x_msg_memory());
  186.  
  187.     /* Update as necessary. */
  188.     read_text_info(obj);
  189.     if(read_width(obj))
  190.       { if(obj->showing) create(obj); }
  191.       else { if(obj->showing) { create_text(obj); redraw_contents(obj); } }
  192.   }
  193. }
  194.  
  195. /* Set the sprite details.  Sprite name (if given) is copied, but we must
  196.    free the sprite area when finished with. */
  197. void iconbar_gadget_set_sprite(iconbar_gadget *obj,
  198.     const sprite_details *sprite)
  199. {
  200.   LOGFUNC(set_sprite);
  201.   if(!obj) return;
  202.  
  203.   /* Don't bother checking whether the sprite name has changed, because the
  204.      underlying sprite might still have been modified. */
  205.  
  206.   /* Clear out old info. */
  207.   destroy_sprite(obj); /* Icon uses pointers to old data. */
  208.   if(!obj->sprite.is_ptr) free(obj->sprite.id.name);
  209.   free(obj->sprite.area);
  210.  
  211.   /* Copy new info. */
  212.   if(sprite->is_ptr)
  213.     obj->sprite.id.ptr = sprite->id.ptr;
  214.   else {
  215.     obj->sprite.id.name = my_strdup(sprite->id.name);
  216.     if(!obj->sprite.id.name && sprite->id.name)
  217.       x_throw_message(x_msg_memory());
  218.   }
  219.   obj->sprite.area = sprite->area;
  220.   obj->sprite.is_ptr = sprite->is_ptr;
  221.  
  222.   /* Update as necessary. */
  223.   find_small_sprite(obj);
  224.   read_sprite_info(obj);
  225.   if(read_width(obj))
  226.     { if(obj->showing) create(obj); }
  227.     else { if(obj->showing) { create_sprite(obj); redraw_contents(obj); } }
  228. }
  229.  
  230. void iconbar_gadget_set_task_name(iconbar_gadget *obj, const char *name)
  231. {
  232.   LOGFUNC(set_task_name);
  233.  
  234.   if(options->use_task_name) {
  235.     free(obj->task_name);
  236.     obj->task_name = my_strdup(name);
  237.     if(!obj->task_name && name) x_throw_message(x_msg_memory());
  238.   }
  239.  
  240.   /* This is a gross hack.  Sorry. */
  241.   if(!obj->text && name && options->use_task_name)
  242.     iconbar_gadget_set_text(obj, name);
  243. }
  244.  
  245.  
  246. /* Internal functions. */
  247.  
  248. /* Returns true if the width has changed.  Before returning, will call the
  249.    resize handler, if necessary, to find the new bounding box.  Will not
  250.    read the new sprite info. */
  251. static bool read_width(iconbar_gadget *obj)
  252. {
  253.   int new_width;
  254.   LOGFUNC(read_width);
  255.  
  256.   /* This depends on how text and sprite are to be arranged. */
  257.   if(options->arrangement == arrangement_TEXT_BOTTOM) {
  258.     /* Text appears underneath sprite. */
  259.     int swidth = obj->sprite_size.x + options->sprite_margin * 2,
  260.     twidth = obj->text_width + options->text_margin * 2;
  261.     new_width = twidth > swidth ? twidth : swidth;
  262.   }
  263.   else {
  264.     /* Text appears to the right of the sprite. */
  265.     new_width = options->icon_h_margins * 2 + obj->sprite_size.x +
  266.     (obj->text ? obj->text_width + options->sprite_text_gap : 0);
  267.   }
  268.   /* Round width to nearest pixel. */
  269.   {
  270.     int xeig;
  271.     os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR, &xeig);
  272.     new_width = (new_width + (1 << xeig) - 1) &~ ((1 << xeig) - 1);
  273.   }
  274.  
  275.   DEBUGF1("Overall width is %i\n", new_width);
  276.  
  277.   if(new_width != (obj->bbox.x1 - obj->bbox.x0) ||
  278.       options->icon_height != (obj->bbox.y1 - obj->bbox.y0)) {
  279.     os_coord new_size;
  280.     new_size.x = new_width;
  281.     new_size.y = options->icon_height;
  282.  
  283.     if(obj->resize_handler) {
  284.       if(obj->resize_handler(obj, &obj->bbox, &new_size,
  285.     obj->resize_handle)) x_throw_message("Error when resizing icon");
  286.     }
  287.     else {
  288.       obj->bbox.x1 = obj->bbox.x0 + new_size.x;
  289.       obj->bbox.y0 = obj->bbox.y1 - new_size.y;
  290.     }
  291.     return 1; /* Width changed. */
  292.   }
  293.   return 0; /* No change. */
  294. }
  295.  
  296. static void read_sprite_info(iconbar_gadget *obj)
  297. {
  298.   os_mode sprite_mode;
  299.   os_error *fail;
  300.   int xeig, yeig;
  301.   LOGFUNC(read_sprite_info);
  302.  
  303.   /* Do we look at the Wimp sprite pool? */
  304.   if(!obj->sprite.area) {
  305.     fail = xwimpspriteop_read_sprite_size(obj->sprite.id.name,
  306.     &obj->sprite_size.x, &obj->sprite_size.y, 0, &sprite_mode);
  307.   }
  308.   else {
  309.     /* Or do we use own own, copied sprite area? */
  310.     if(!obj->sprite.is_ptr) {
  311.       fail = xosspriteop_read_sprite_size(osspriteop_NAME,
  312.     obj->sprite.area, (osspriteop_id) obj->sprite.id.name,
  313.     &obj->sprite_size.x, &obj->sprite_size.y, 0, &sprite_mode);
  314.     }
  315.     else {
  316.       fail = xosspriteop_read_sprite_size(osspriteop_PTR,
  317.     obj->sprite.area, obj->sprite.id.ptr,
  318.     &obj->sprite_size.x, &obj->sprite_size.y, 0, &sprite_mode);
  319.     }
  320.   }
  321.  
  322.   /* If the sprite was broken and the SWI failed, drop back to
  323.      sensible default sizes */
  324.   if(fail) {
  325.     obj->sprite_size.x = obj->sprite_size.y = 68;
  326.     DEBUGF2("Reading sprite size failed; using defaults (%i by %i)\n",
  327.     obj->sprite_size.x, obj->sprite_size.y);
  328.   }
  329.   else {
  330.     /* And then convert pixels to OS units */
  331.     os_read_mode_variable(sprite_mode, os_MODEVAR_XEIG_FACTOR, &xeig);
  332.     os_read_mode_variable(sprite_mode, os_MODEVAR_YEIG_FACTOR, &yeig);
  333.     obj->sprite_size.x <<= xeig;
  334.     obj->sprite_size.y <<= yeig;
  335.     DEBUGF2("Sprite size is %i by %i\n",
  336.     obj->sprite_size.x, obj->sprite_size.y);
  337.   }
  338.  
  339.   /* Take account of half size sprites option. */
  340.   if(options->small_icons && !obj->sprite_small) {
  341.     obj->sprite_size.x /= 2;
  342.     obj->sprite_size.y /= 2;
  343.   }
  344. }
  345.  
  346. static void read_text_info(iconbar_gadget *obj)
  347. {
  348.   LOGFUNC(read_text_info);
  349.   obj->text_width = obj->text ?
  350.     font_text_width(obj->font, obj->text) : 0;
  351.   DEBUGF1("Text width is %i\n", obj->text_width);
  352. }
  353.  
  354. static void create(iconbar_gadget *obj)
  355. {
  356.   LOGFUNC(create);
  357.   create_border(obj);
  358.   create_sprite(obj);
  359.   create_text(obj);
  360.   obj->showing = 1;
  361. }
  362.  
  363. static void button_template(gadget_object **gadget, button_gadget **button)
  364. {
  365.   *gadget = malloc(sizeof(gadget_object) + sizeof(button_gadget));
  366.   *button = (button_gadget *) (*gadget)->gadget;
  367.   if(!*gadget) x_throw_message(x_msg_memory());
  368.  
  369.   (*gadget)->flags = button_ALLOW_MENU_CLICKS;
  370.   (*gadget)->class_no = class_BUTTON;
  371.   (*gadget)->size = 6*4 + 5*4;
  372.   (*gadget)->cmp = -1;
  373.   (*gadget)->help_message = 0;
  374.   (*gadget)->help_limit = 0;
  375. }
  376.  
  377. static void create_border(iconbar_gadget *obj)
  378. {
  379.   gadget_object *gadget;
  380.   button_gadget *button;
  381.   LOGFUNC(create_border);
  382.  
  383.   destroy_border(obj);
  384.   button_template(&gadget, &button);
  385.  
  386.   /* Create border component. */
  387.   gadget->bbox = obj->bbox;
  388.   button->flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
  389.     (options->icon_borders != border_type_NONE ? wimp_ICON_BORDER : 0) |
  390.     (options->icon_bg_colour != -1 ? wimp_ICON_FILLED : 0) |
  391.     wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
  392.     (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT) |
  393.     (options->text_colour << wimp_ICON_FG_COLOUR_SHIFT) |
  394.     ((options->icon_bg_colour != -1 ? options->icon_bg_colour :
  395.         options->window_bg_colour) << wimp_ICON_BG_COLOUR_SHIFT);
  396.   button->value = "";
  397.   button->value_limit = 1;
  398.   /* The border appearance is configurable. */
  399.   switch(options->icon_borders) {
  400.     case border_type_SLAB_OUT:    button->validation = "r1"; break;
  401.     case border_type_SLAB_IN:    button->validation = "r2"; break;
  402.     case border_type_NONE:
  403.     case border_type_PLAIN:
  404.     default:            button->validation = 0; break;
  405.   }
  406.   button->validation_limit = 4;
  407.   obj->border_cmp = window_add_gadget(0, obj->window, gadget);
  408.   free(gadget);
  409. }
  410.  
  411. static void create_sprite(iconbar_gadget *obj)
  412. {
  413.   /* Create a single-component, sprite icon as a Wimp icon. */
  414.   /* We have to do this because the Toolbox won't let us create Button
  415.      gadgets with sprites in different pools.  D'oh! */
  416.   wimp_icon_create i;
  417.   LOGFUNC(create_sprite);
  418.   destroy_sprite(obj);
  419.  
  420.   i.w = window_get_wimp_handle(0, obj->window);
  421.   /* Work out bounding box. */
  422.   /* This depends on where the text is to be placed. */
  423.   if(options->arrangement == arrangement_TEXT_BOTTOM) {
  424.     /* Text goes at the bottom, so the sprite goes just above.  If the sprite
  425.        is big enough that it would overlap the top, it is allowed to overlap
  426.        the text instead. */
  427.     i.icon.extent.x0 = obj->bbox.x0 + options->border_size;
  428.     i.icon.extent.x1 = obj->bbox.x1 - options->border_size;
  429.     if(obj->text) {
  430.       int top;
  431.       i.icon.extent.y0 = obj->bbox.y0 +
  432.         options->border_size + options->text_height;
  433.       i.icon.extent.y1 = i.icon.extent.y0 + obj->sprite_size.y;
  434.       top = obj->bbox.y1 - options->border_size;
  435.       if(i.icon.extent.y1 > top) {
  436.         i.icon.extent.y1 = top;
  437.         i.icon.extent.y0 = top - obj->sprite_size.y;
  438.       }
  439.     }
  440.     else {
  441.       i.icon.extent.y0 = obj->bbox.y0 + options->border_size;
  442.       i.icon.extent.y1 = obj->bbox.y1 - options->border_size;
  443.     }
  444.   }
  445.   else {
  446.     /* Text goes to the right of the sprite.  The sprite's position is fixed
  447.        to the left of the bounding box, and centred vertically. */
  448.     i.icon.extent.x0 = obj->bbox.x0 + options->icon_h_margins;
  449.     i.icon.extent.x1 = i.icon.extent.x0 + obj->sprite_size.x;
  450.     i.icon.extent.y0 = obj->bbox.y0 + options->border_size;
  451.     i.icon.extent.y1 = obj->bbox.y1 - options->border_size;
  452.   }
  453.   /* Icon flags. */
  454.   i.icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
  455.     wimp_ICON_VCENTRED | wimp_ICON_HCENTRED |
  456.     (options->small_icons && !obj->sprite_small ?
  457.         wimp_ICON_HALF_SIZE : 0) |
  458.     (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT) |
  459.     (options->text_colour << wimp_ICON_FG_COLOUR_SHIFT) |
  460.     ((options->icon_bg_colour != -1 ? options->icon_bg_colour :
  461.         options->window_bg_colour) << wimp_ICON_BG_COLOUR_SHIFT);
  462.   /* Icon data. */
  463.   i.icon.data.indirected_sprite.id = obj->sprite.is_ptr ?
  464.     obj->sprite.id.ptr : (osspriteop_id) obj->sprite.id.name;
  465.   i.icon.data.indirected_sprite.area = (obj->sprite.area ?
  466.     obj->sprite.area : wimpspriteop_AREA);
  467.   i.icon.data.indirected_sprite.size = obj->sprite.is_ptr ? 0 : 12;
  468.   /* Create the icon. */
  469.   obj->sprite_icon = wimp_create_icon(&i);
  470. }
  471.  
  472. static void create_text(iconbar_gadget *obj)
  473. {
  474.   LOGFUNC(create_text);
  475.   destroy_text(obj);
  476.  
  477.   if(obj->text) {
  478.     int font_handle;
  479.     wimp_icon_create i;
  480.     i.w = window_get_wimp_handle(0, obj->window);
  481.  
  482.     /* Bounding box. */
  483.     /* This depends on how text and sprite are to be arranged. */
  484.     if(options->arrangement == arrangement_TEXT_BOTTOM) {
  485.       /* Text position is fixed at the bottom of the bounding box. */
  486.       i.icon.extent.x0 = obj->bbox.x0 + options->border_size;
  487.       i.icon.extent.x1 = obj->bbox.x1 - options->border_size;
  488.       i.icon.extent.y0 = obj->bbox.y0 + options->border_size;
  489.       i.icon.extent.y1 = i.icon.extent.y0 + options->text_height;
  490.     }
  491.     else {
  492.       /* Text position is just right of the sprite, and centred
  493.          vertically. */
  494.       /* Allow options->icon_h_margins extra space either side of the text
  495.          box, because the Wimp tries to align the text within its box, and
  496.          we don't want it to do that. */
  497.       i.icon.extent.x0 = obj->bbox.x0 + obj->sprite_size.x +
  498.         options->sprite_text_gap;
  499.       i.icon.extent.x1 = i.icon.extent.x0 + obj->text_width +
  500.         options->icon_h_margins * 2;
  501.       i.icon.extent.y0 = obj->bbox.y0 + options->border_size;
  502.       i.icon.extent.y1 = obj->bbox.y1 - options->border_size;
  503.     }
  504.     /* Flags. */
  505.     font_handle = font_get_handle(obj->font);
  506.     i.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
  507.     (options->arrangement == arrangement_TEXT_BOTTOM ?
  508.         wimp_ICON_HCENTRED : 0) |
  509.     wimp_ICON_VCENTRED |
  510.     (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT) |
  511.     (font_handle ?
  512.         (font_handle << wimp_ICON_FONT_HANDLE_SHIFT) |
  513.         wimp_ICON_ANTI_ALIASED :
  514.         (options->text_colour << wimp_ICON_FG_COLOUR_SHIFT) |
  515.         ((options->icon_bg_colour != -1 ? options->icon_bg_colour :
  516.           options->window_bg_colour) << wimp_ICON_BG_COLOUR_SHIFT));
  517.     /* Data. */
  518.     i.icon.data.indirected_text.text = obj->text;
  519.     i.icon.data.indirected_text.size = strlen(obj->text) + 1;
  520.     /* Use validation string for font colours. */
  521.     if(font_handle) {
  522.       char *v = obj->text_valid = malloc(4);
  523.       if(!v) x_throw_message(x_msg_memory());
  524.       v[0] = 'f';
  525. #define HEX_DIGIT(n)            \
  526.     ((n) >= 10 ? 'a' + (n) - 10 :    \
  527.         '0' + (n))
  528.       v[1] = HEX_DIGIT(options->icon_bg_colour != -1 ?
  529.         options->icon_bg_colour : options->window_bg_colour);
  530.       v[2] = HEX_DIGIT(options->text_colour);
  531. #undef HEX_DIGIT
  532.       v[3] = 0;
  533.       i.icon.data.indirected_text.validation = v;
  534.     }
  535.     else i.icon.data.indirected_text.validation = 0;
  536.     /* Create it. */
  537.     obj->text_icon = wimp_create_icon(&i);
  538.   }
  539. }
  540.  
  541. static void destroy(iconbar_gadget *obj)
  542. {
  543.   LOGFUNC(destroy);
  544.   if(!obj->showing) return;
  545.  
  546.   destroy_border(obj);
  547.   destroy_sprite(obj);
  548.   destroy_text(obj);
  549.   obj->showing = 0;
  550. }
  551.  
  552. static void destroy_border(iconbar_gadget *obj)
  553. {
  554.   LOGFUNC(destroy_border);
  555.   if(obj->border_cmp == -1) return;
  556.   window_remove_gadget(0, obj->window, obj->border_cmp);
  557.   obj->border_cmp = -1;
  558. }
  559.  
  560. static void destroy_sprite(iconbar_gadget *obj)
  561. {
  562.   LOGFUNC(destroy_sprite);
  563.   if(obj->sprite_icon == -1) return;
  564.   wimp_delete_icon(window_get_wimp_handle(0, obj->window), obj->sprite_icon);
  565.   obj->sprite_icon = -1;
  566. }
  567.  
  568. static void destroy_text(iconbar_gadget *obj)
  569. {
  570.   LOGFUNC(destroy_text);
  571.   if(obj->text_icon == -1) return;
  572.   wimp_delete_icon(window_get_wimp_handle(0, obj->window), obj->text_icon);
  573.   obj->text_icon = -1;
  574.  
  575.   free(obj->text_valid);
  576.   obj->text_valid = 0;
  577. }
  578.  
  579. /* Redraws the contents of an icon (text and sprite), but not border, to
  580.    avoid flicker. */
  581. static void redraw_contents(iconbar_gadget *obj)
  582. {
  583.   wimp_draw draw;
  584.   bool more;
  585.   LOGFUNC(redraw_contents);
  586.  
  587.   draw.w = window_get_wimp_handle(0, obj->window);
  588.   draw.box.x0 = obj->bbox.x0 + options->border_size;
  589.   draw.box.y0 = obj->bbox.y0 + options->border_size;
  590.   draw.box.x1 = obj->bbox.x1 - options->border_size;
  591.   draw.box.y1 = obj->bbox.y1 - options->border_size;
  592.  
  593.   more = wimp_update_window(&draw);
  594.   while(more) more = wimp_get_rectangle(&draw);
  595. }
  596.  
  597. /* If the small sprites option is set, this function looks for a small
  598.    version of the existing sprite, and if one exists, makes a note of
  599.    this. */
  600. static void find_small_sprite(iconbar_gadget *obj)
  601. {
  602.   LOGFUNC(find_small_sprite);
  603.   if(options->small_icons && !obj->sprite.area && !obj->sprite.is_ptr) {
  604.     /* Look up sprite `smfoo' where `foo' is current sprite. */
  605.     int current_len = strlen(obj->sprite.id.name);
  606.     char *spr = malloc(2 + current_len + 1);
  607.     if(!spr) x_throw_message(x_msg_memory());
  608.     spr[0] = 's';
  609.     spr[1] = 'm';
  610.     memcpy(spr + 2, obj->sprite.id.name, current_len + 1);
  611.     /* SWI should give error if sprite does not exist. */
  612.     if(!xwimpspriteop_read_sprite_size(spr, 0, 0, 0, 0)) {
  613.       /* Old sprite name is replaced, and flag set. */
  614.       free(obj->sprite.id.name);
  615.       obj->sprite.id.name = spr;
  616.       obj->sprite_small = 1;
  617.     }
  618.     else {
  619.       free(spr);
  620.       obj->sprite_small = 0;
  621.     }
  622.   }
  623.   else obj->sprite_small = 0;
  624. }
  625.  
  626.  
  627. /* Useful functions. */
  628.  
  629. /* Duplicates a string.  Can be passed 0, may return 0. */
  630. char *my_strdup(const char *s)
  631. {
  632.   int len;
  633.   char *n;
  634.   if(!s) return 0;
  635.  
  636.   len = strlen(s);
  637.   n = malloc(len + 1);
  638.   if(!n) return 0;
  639.   memcpy(n, s, len + 1);
  640.   return n;
  641. }
  642.