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

  1.  
  2. /* iconmgr.c */
  3.  
  4. #include <stdlib.h>
  5. #include "OS:os.h"
  6. #include "OS:osmodule.h"
  7. #include "OS:wimp.h"
  8. #include "OS:toolbox.h"
  9. #include "OS:window.h"
  10. #include "OS:taskmanager.h"
  11. #include "Dreamscape:task.h"
  12. #include "Dreamscape:wimpevent.h"
  13. #include "Dreamscape:wimpmsg.h"
  14. #include "Dreamscape:x.h"
  15.  
  16. #ifdef MemCheck_MEMCHECK
  17. #include "MemCheck:MemCheck.h"
  18. #endif
  19.  
  20. #include "iconmgr.h"
  21. #include "debug.h"
  22. #include "options.h"
  23. #include "various.h"
  24.  
  25.  
  26. /* Private structures */
  27.  
  28. typedef struct iconbar_manager_icon iconbar_manager_icon;
  29. typedef struct iconbar_manager_node iconbar_manager_node;
  30.  
  31. struct iconbar_manager_icon {
  32.   ibarpatch_add_info *ibarpatch_block; /* To update later. */
  33.   iconbar_gadget *gadget;
  34.   void *external_handle; /* Used by the icon arranger. */
  35.  
  36.   /* Information about icon from IconbarPatch. */
  37.   int icon_handle;
  38.   wimp_t task;
  39.   wimp_icon_flags flags;
  40.   wimp_icon_data data;
  41.  
  42.   /* Data we've worked out. */
  43.   int priority;
  44.   unsigned update_poll_id;
  45. };
  46.  
  47. struct iconbar_manager_node {
  48.   iconbar_manager_icon i;
  49.   iconbar_manager_node *next;
  50. };
  51.  
  52. struct iconbar_manager_task {
  53.   ibarpatch_block *module;
  54.   iconbar_manager_node *icons;
  55.  
  56.   iconbar_arranger *arranger;
  57.   unsigned poll_id;
  58.   iconbar_manager_icon *pointer_icon; /* Icon under pointer is cached. */
  59. };
  60.  
  61.  
  62. /* Internal function prototypes */
  63.  
  64. #define MODULE_NAME    "iconbar_manager"
  65.  
  66. /* Event handlers */
  67. static bool click_handler(const wimp_block *event,
  68.     const toolbox_block *ids, void *handle);
  69. static bool pollword_handler(const wimp_block *event,
  70.     const toolbox_block *ids, void *handle);
  71. static bool null_event_handler(const wimp_block *event,
  72.     const toolbox_block *ids, void *handle);
  73. static bool task_quitting_handler(const wimp_message *message, void *handle);
  74.  
  75. static iconbar_manager_icon *icon_from_pointer(iconbar_manager_task *task,
  76.     const os_coord *pos, toolbox_o window, wimp_w wimp_window);
  77. static bool process_ibarpatch_event(iconbar_manager_task *task,
  78.     ibarpatch_element *e);
  79.  
  80. static void add_icon(iconbar_manager_task *task, ibarpatch_add_info *i,
  81.     const char *task_name);
  82. static void update_icon(iconbar_manager_task *task,
  83.     iconbar_manager_icon *icon);
  84. static void remove_icon(iconbar_manager_task *task,
  85.     iconbar_manager_node **node_ptr);
  86.  
  87.  
  88. /* Initialisation and finalisation */
  89.  
  90. iconbar_manager_task *iconbar_manager_initialise(iconbar_arranger *arranger)
  91. {
  92.   iconbar_manager_task *task = malloc(sizeof(iconbar_manager_task));
  93.   LOGFUNC(initialise);
  94.   if(!task) return 0;
  95.  
  96.   /* Initialise variables. */
  97.   task->icons = 0;
  98.   task->arranger = arranger;
  99.   task->poll_id = 0;
  100.   task->pointer_icon = 0;
  101.  
  102.   /* Find module's block and increment usage count. */
  103.   osmodule_lookup("IconbarPatch", 0, 0, (void **) &task->module, 0);
  104. #ifdef MemCheck_MEMCHECK
  105.   MemCheck_RegisterMiscBlock(task->module, sizeof(ibarpatch_block));
  106. #endif
  107.   ++task->module->usage_count;
  108.  
  109.   /* Wimp event handlers. */
  110.   dscape_wimpevent_register_handler(wimp_MOUSE_CLICK, 0,
  111.     click_handler, task);
  112.   dscape_wimpevent_register_handler(wimp_NULL_REASON_CODE, 0,
  113.     null_event_handler, task);
  114.   /* Set up our poll word non-zero handler (with low priority) */
  115.   dscape_task_set_poll_word((int *) &task->module->list.begin, 0);
  116.   dscape_wimpevent_register_handler(wimp_POLLWORD_NON_ZERO,
  117.     0, pollword_handler, task);
  118.  
  119.   /* Wimp message handlers. */
  120.   dscape_wimpmsg_register_handler(message_TASK_CLOSE_DOWN,
  121.     task_quitting_handler, task);
  122.  
  123.   /* Read list of previous icons set up. */
  124.   {
  125.     ibarpatch_element *e = task->module->list_keep.begin, *next;
  126.     while(e) {
  127. #ifdef MemCheck_MEMCHECK
  128.       MemCheck_RegisterMiscBlock(e, sizeof(ibarpatch_element));
  129. #endif
  130.       process_ibarpatch_event(task, e); /* Ignore return value */
  131.       next = e->next;
  132. #ifdef MemCheck_MEMCHECK
  133.       MemCheck_UnRegisterMiscBlock(e);
  134. #endif
  135.       e = next;
  136.     }
  137.   }
  138.   return task;
  139. }
  140.  
  141. void iconbar_manager_finalise(iconbar_manager_task *task)
  142. {
  143.   LOGFUNC(finalise);
  144.   if(!task) return;
  145.  
  146.   /* Delete all icons. */
  147.   while(task->icons) {
  148.     iconbar_manager_node *old = task->icons;
  149.     task->icons = old->next;
  150.  
  151.     /* Finalise this icon, and free the node. */
  152.     task->arranger->remove_icon(task->arranger, old->i.gadget,
  153.         old->i.external_handle);
  154.     iconbar_gadget_destroy(old->i.gadget);
  155.     /* Don't remove icon from keep list. */
  156.     free(old);
  157.   }
  158.  
  159.   /* Reset pointer values if necessary. */
  160.   if(task_handle_from_window(task->module->window, -1) ==
  161.     dscape_task_get_handle()) {
  162.     task->module->window = 0;
  163.     task->module->pointer_icon = -1;
  164.     task->module->pointer_task = 0;
  165.   }
  166.  
  167.   /* Wimp message handlers. */
  168.   dscape_wimpmsg_deregister_handler(message_TASK_CLOSE_DOWN,
  169.     task_quitting_handler, task);
  170.  
  171.   /* Wimp event handlers. */
  172.   dscape_wimpevent_deregister_handler(wimp_MOUSE_CLICK, 0,
  173.     click_handler, task);
  174.   dscape_wimpevent_deregister_handler(wimp_NULL_REASON_CODE, 0,
  175.     null_event_handler, task);
  176.   /* Poll word non-zero handler. */
  177.   dscape_task_set_poll_word(0, 0);
  178.   dscape_wimpevent_deregister_handler(wimp_POLLWORD_NON_ZERO,
  179.     0, pollword_handler, task);
  180.  
  181.   /* We have finished with the module. */
  182.   --task->module->usage_count;
  183. #ifdef MemCheck_MEMCHECK
  184.   MemCheck_UnRegisterMiscBlock(task->module);
  185. #endif
  186.  
  187.   free(task);
  188. }
  189.  
  190.  
  191. /* Event handlers */
  192.  
  193. /* Handle clicks on icons. */
  194. static bool click_handler(const wimp_block *event,
  195.     const toolbox_block *ids, void *xhandle)
  196. {
  197.   iconbar_manager_task *task = xhandle;
  198.   iconbar_manager_icon *icon;
  199.   LOGFUNC(click_handler);
  200.  
  201.   /* Ignore if Menu was clicked at the very bottom of the screen.  This
  202.      allows the `Toggle' module to still work. */
  203.   if(event->pointer.buttons == 2 && event->pointer.pos.y == 0)
  204.     return 0; /* Pass event on */
  205.  
  206.   icon = icon_from_pointer(task, &event->pointer.pos, ids->this_obj,
  207.     event->pointer.w);
  208.   if(icon) {
  209.     /* Re-send the mouse click event to the task in question. */
  210.     wimp_block message = *event;
  211.     DEBUGF1("Sending mouse click to task (buttons %i)\n",
  212.         event->pointer.buttons);
  213.     message.pointer.w = wimp_ICON_BAR;
  214.     message.pointer.i = icon->icon_handle;
  215.     wimp_send_message(wimp_MOUSE_CLICK,
  216.     (wimp_message *) &message, icon->task);
  217.  
  218.     /* If Menu was clicked, alter where the menu will appear. */
  219.     if(event->pointer.buttons == 2) {
  220.       /* Open the base of the menu a given distance below the top of the
  221.          icon.  (NB. 96 is the position menu bases would otherwise be
  222.          opened at.) */
  223.       os_box bbox;
  224.       wimp_window_state state;
  225.  
  226.       iconbar_gadget_get_bbox(icon->gadget, &bbox);
  227.       state.w = window_get_wimp_handle(0,
  228.     iconbar_gadget_get_window(icon->gadget));
  229.       wimp_get_window_state(&state);
  230.  
  231.       task->module->y_offset = (bbox.y1 + state.visible.y1 - state.yscroll)
  232.     - (96 + options->menu_y_offset);
  233.     }
  234.     return 1; /* Claim event */
  235.   }
  236.   return 0; /* Pass event on */
  237. }
  238.  
  239. /* For a given mouse state (the pointer position and window it is over),
  240.    returns which icon the pointer is over, if any.  If no pointer position is
  241.    given, it reads the current state (including window). */
  242. static iconbar_manager_icon *icon_from_pointer(iconbar_manager_task *task,
  243.     const os_coord *pos, toolbox_o window, wimp_w wimp_window)
  244. {
  245.   iconbar_manager_node *node;
  246.   os_coord pos_read,
  247.     work_pos; /* Work area pointer position */
  248.   LOGFUNC(icon_from_pointer);
  249.  
  250.   /* Read the mouse state if none was passed. */
  251.   if(!pos) {
  252.     wimp_w current;
  253.     wimp_pointer ptr;
  254.     /* We have to tell IconbarPatch not to fiddle with returned data.  This
  255.        might be necessary for Window_WimpToToolbox too, since it *could*
  256.        send a message to the window to find its owning task first (not
  257.        likely, but possible). */
  258.     iconbar_manager_get_pointer_info(task, &ptr);
  259.  
  260.     /* Pointer position. */
  261.     pos_read = ptr.pos;
  262.     pos = &pos_read;
  263.     /* Pointer window. */
  264.     wimp_window = ptr.w;
  265.     /* This next call was giving me address exceptions at seemingly random
  266.        times.  This could either be a Toolbox bug, or it could be returning
  267.        an error and the error handler went pear-shaped (although this SWI
  268.        should not give an error).  As X-form, it seems to work.  Also, don't
  269.        tell it the icon handle (why should we?). */
  270.     current = task->module->window;
  271.     task->module->window = 0;
  272.     if(xwindow_wimp_to_toolbox(0, ptr.w, -1, &window, 0))
  273.       window = 0; /* Just in case. */
  274.     task->module->window = current;
  275.   }
  276.  
  277.   /* Only care about this task's Toolbox windows. */
  278.   if(!window) return task->pointer_icon = 0; /* Not over icon */
  279.  
  280.   /* Find pointer position in window's work area. */
  281.   {
  282.     wimp_window_state state;
  283.     state.w = wimp_window;
  284.     wimp_get_window_state(&state);
  285.     work_pos.x = pos->x - state.visible.x0 + state.xscroll;
  286.     work_pos.y = pos->y - state.visible.y1 + state.yscroll;
  287.   }
  288.  
  289.   /* Check last cached icon under pointer. */
  290.   if(task->pointer_icon &&
  291.       iconbar_gadget_get_window(task->pointer_icon->gadget) == window) {
  292.     os_box bbox;
  293.     iconbar_gadget_get_bbox(task->pointer_icon->gadget, &bbox);
  294.     if(work_pos.x >= bbox.x0 && work_pos.x < bbox.x1 &&
  295.        work_pos.y >= bbox.y0 && work_pos.y < bbox.y1)
  296.     return task->pointer_icon;
  297.   }
  298.  
  299.   /* Go through list of icons to find a match for both window and bbox. */
  300.   for(node = task->icons; node; node = node->next) {
  301.     if(iconbar_gadget_get_window(node->i.gadget) == window) {
  302.       os_box bbox;
  303.       iconbar_gadget_get_bbox(node->i.gadget, &bbox);
  304.       if(work_pos.x >= bbox.x0 && work_pos.x < bbox.x1 &&
  305.          work_pos.y >= bbox.y0 && work_pos.y < bbox.y1) {
  306.         DEBUGF1("Icon under pointer is number %i\n", node->i.icon_handle);
  307.         return task->pointer_icon = &node->i;
  308.       }
  309.     }
  310.   }
  311.   return task->pointer_icon = 0; /* Not over icon */
  312. }
  313.  
  314. /* Does wimp_get_pointer_info without fudging window handles, etc. */
  315. void iconbar_manager_get_pointer_info(iconbar_manager_task *task,
  316.     wimp_pointer *pointer)
  317. {
  318.   wimp_w current;
  319.   LOGFUNC(get_pointer_info);
  320.   if(!task) return;
  321.  
  322.   /* Unsets the iconbar window temporarily so that it looks like a
  323.      normal window. */
  324.   current = task->module->window;
  325.   task->module->window = 0;
  326.   wimp_get_pointer_info(pointer);
  327.   task->module->window = current;
  328. }
  329.  
  330. /* Defines a function and macro to check the integrity of one of the
  331.    IconbarPatch module's event lists.  The check is done for MemCheck and
  332.    debug builds, and is typically done after the list has been modified.
  333.    It is impossible to use MemCheck to check our handling of the lists
  334.    (because we don't create the blocks), and I would like peace of mind that
  335.    they aren't being corrupted. */
  336. #if defined MemCheck_MEMCHECK || defined DEBUG
  337. #  define PFX    "List-check: "
  338. static void check_module_list_integrity(ibarpatch_list *list,
  339.     const char *name)
  340. {
  341.   char *rma;
  342.   int rma_size, count = 0;
  343.   ibarpatch_element **e;
  344.   bool fail = 0;
  345.   DEBUGF2(PFX "Checking list `%s' (%i blocks recorded)\n",
  346.     name, list->count);
  347.  
  348.   /* Find where the module area is (all blocks must be in the RMA). */
  349.   rma = (char *) os_read_dynamic_area(os_DYNAMIC_AREA_RMA, &rma_size, 0);
  350.  
  351. #ifdef MemCheck_MEMCHECK
  352.   /* It is easiest to turn checking off throughout this. */
  353.   MemCheck_SetChecking(0, 0);
  354. #endif
  355.   /* Go through the list. */
  356.   e = &list->begin;
  357.   while(1) {
  358.     if(!*e) {
  359.       /* The last element:  make sure this tallies. */
  360.       if(e != list->end) {
  361.         fprintf(stderr, PFX "List end doesn't tally (%i blocks found)\n",
  362.         count);
  363.         fail = 1;
  364.       }
  365.       break;
  366.     }
  367.     /* Check that the block pointer is in the module area. */
  368.     if((char *) *e < rma || (char *) *e >= (rma + rma_size)) {
  369.       /* End the check since it will lead to never-never land. */
  370.       fprintf(stderr, PFX "Block %i not in module area (check aborted)\n",
  371.         count);
  372.       fail = 1;
  373.       break;
  374.     }
  375.     ++count;
  376.     e = &(*e)->next;
  377.   }
  378. #ifdef MemCheck_MEMCHECK
  379.   MemCheck_SetChecking(1, 1);
  380. #endif
  381.   /* Check that block counts match. */
  382.   if(count != list->count) {
  383.     fprintf(stderr, PFX "Block count mismatch (%i recorded, %i found)\n",
  384.     list->count, count);
  385.     fail = 1;
  386.   }
  387.   if(fail) {
  388.     fprintf(stderr, PFX "List `%s' failed check\n", name);
  389.     abort();
  390.   }
  391. }
  392. #  undef PFX
  393. #  define CHECK_INTEGRITY(list, name) check_module_list_integrity(list, name)
  394. #else
  395. #  define CHECK_INTEGRITY(list, name) ((void) 0)
  396. #endif
  397. #define CHECK_EVENT_LIST(mod)    CHECK_INTEGRITY(&mod->list, "event")
  398. #define CHECK_KEEP_LIST(mod)    CHECK_INTEGRITY(&mod->list_keep, "keep")
  399.  
  400. /* Handle any new events from IconbarPatch. */
  401. static bool pollword_handler(const wimp_block *event,
  402.     const toolbox_block *ids, void *xhandle)
  403. {
  404.   iconbar_manager_task *task = xhandle;
  405.   LOGFUNC(pollword_handler);
  406.   ++task->poll_id;
  407.  
  408.   CHECK_EVENT_LIST(task->module);
  409.   CHECK_KEEP_LIST(task->module);
  410.  
  411.   /* Handle any events queued by the module */
  412.   while(task->module->list.begin) {
  413.     bool remove;
  414.     ibarpatch_element *e = task->module->list.begin;
  415. #ifdef MemCheck_MEMCHECK
  416.     MemCheck_RegisterMiscBlock(e, sizeof(ibarpatch_element));
  417. #endif
  418.  
  419.     /* Handle the event. */
  420.     remove = process_ibarpatch_event(task, e);
  421.  
  422.     /* An add event is a useful record, so move it to the keep list.
  423.        It goes at the end of the list so that icons can be re-created in
  424.        order.  (Unless the task died, in which case throw it away. */
  425.     if(e->type == ibarpatch_add_type && !remove) {
  426.       /* Unlink event from list. */
  427.       task->module->list.begin = e->next;
  428.       if(task->module->list.end == &e->next)
  429.         task->module->list.end = &task->module->list.begin;
  430.       /* Add it to the keep list. */
  431. #ifdef MemCheck_MEMCHECK
  432.       MemCheck_RegisterMiscBlock(task->module->list_keep.end, 4);
  433. #endif
  434.       *task->module->list_keep.end = e;
  435. #ifdef MemCheck_MEMCHECK
  436.       MemCheck_UnRegisterMiscBlock(task->module->list_keep.end);
  437. #endif
  438.       e->next = 0;
  439.       task->module->list_keep.end = &e->next;
  440.       --task->module->list.count;
  441.       ++task->module->list_keep.count;
  442.       CHECK_EVENT_LIST(task->module);
  443.       CHECK_KEEP_LIST(task->module);
  444.     }
  445.  
  446.     /* Update and remove events will be deleted from list.  Mostly add events
  447.        will be kept, but out-of-date ones are deleted. */
  448.     if(remove) {
  449.       /* Remove event from list. */
  450.       task->module->list.begin = e->next;
  451.       if(task->module->list.end == &e->next)
  452.         task->module->list.end = &task->module->list.begin;
  453.       osmodule_free(e);
  454.       --task->module->list.count;
  455.       CHECK_EVENT_LIST(task->module);
  456.     }
  457.  
  458. #ifdef MemCheck_MEMCHECK
  459.     MemCheck_UnRegisterMiscBlock(e);
  460. #endif
  461.   }
  462.   return 1;
  463. }
  464.  
  465. /* Deals with an IconbarPatch event.  Does not unlink the event from the
  466.    list of events (this is the task of the caller), but returns true if the
  467.    event should be deleted. */
  468. static bool process_ibarpatch_event(iconbar_manager_task *task,
  469.     ibarpatch_element *e)
  470. {
  471.   bool remove = 0;
  472.   x_declare(error);
  473.   LOGFUNC(process_ibarpatch_event);
  474.   x_try switch(e->type) {
  475.  
  476.   /* Add an iconbar icon. */
  477.   case ibarpatch_add_type: {
  478.     /* Check to see if the task still exists.  If it doesn't, it's likely
  479.        that it died as soon as it started up, so we don't want to display
  480.        its icon. */
  481.     char *task_name;
  482.     os_error *x = xtaskmanager_task_name_from_handle(e->data.add.task,
  483.             &task_name);
  484.     /* The error number the Task Manager seems to return for `Task not
  485.        found' is 3.  We don't want to fail for any old error, because
  486.        the Task Manager might not be present (eg. on an NC). */
  487. #ifdef MemCheck_MEMCHECK
  488.     if(x) MemCheck_RegisterMiscBlock(x, sizeof(os_error));
  489. #endif
  490.     if(!(x && x->errnum == 3)) {
  491.       if(!x) {
  492.         /* Copy the task name.  I wish the PRMs would say how strings are
  493.            terminated.  I have to copy it on the off-chance it isn't
  494.            null-terminated. */
  495.         int len = 0;
  496.         char *b;
  497. #ifdef MemCheck_MEMCHECK
  498.         MemCheck_RegisterMiscBlock(task_name, 256);
  499. #endif
  500.         while(task_name[len] >= 32 && len < 256) ++len;
  501.         b = malloc(len + 1);
  502.         if(!b) x_throw_message(x_msg_memory());
  503.         memcpy(b, task_name, len);
  504. #ifdef MemCheck_MEMCHECK
  505.         MemCheck_UnRegisterMiscBlock(task_name);
  506. #endif
  507.         b[len] = 0;
  508.         task_name = b;
  509.       }
  510.       else {
  511.         task_name = 0;
  512.       }
  513.       DEBUGF2("Adding new icon, number %i (task `%s')\n",
  514.         e->data.add.icon_handle, task_name ? task_name : "");
  515.       add_icon(task, &e->data.add, task_name);
  516.       free(task_name);
  517.     }
  518.     else {
  519.       DEBUGF1("Icon number %i not added (task has died)\n",
  520.     e->data.add.icon_handle);
  521.       remove = 1;
  522.     }
  523. #ifdef MemCheck_MEMCHECK
  524.     if(x) MemCheck_UnRegisterMiscBlock(x);
  525. #endif
  526.     } break;
  527.  
  528.   /* Remove an iconbar icon. */
  529.   case ibarpatch_remove_type: {
  530.     iconbar_manager_node **find;
  531.     remove = 1;
  532.     DEBUGF1("Removing icon, number %i\n", e->data.remove.icon_handle);
  533.     for(find = &task->icons; *find; find = &(*find)->next)
  534.       if((*find)->i.icon_handle == e->data.remove.icon_handle)
  535.         { remove_icon(task, find); break; }
  536.     } break;
  537.  
  538.   /* Update an iconbar icon. */
  539.   case ibarpatch_update_type: {
  540.     iconbar_manager_node *find;
  541.     remove = 1;
  542.     DEBUGF1("Updating icon, number %i\n", e->data.update.icon_handle);
  543.     for(find = task->icons; find; find = find->next)
  544.         if(find->i.icon_handle == e->data.update.icon_handle) {
  545.       /* Don't bother updating the icon again if it was just updated. */
  546.       if(!e->data.update.bic && !e->data.update.eor &&
  547.     find->i.update_poll_id == task->poll_id) break;
  548.       find->i.flags = (find->i.flags &~ e->data.update.bic)
  549.         ^ e->data.update.eor;
  550.       /* Copy flags back, for the record. */
  551. #ifdef MemCheck_MEMCHECK
  552.       MemCheck_RegisterMiscBlock(find->i.ibarpatch_block,
  553.         sizeof(ibarpatch_add_info));
  554. #endif
  555.       find->i.ibarpatch_block->flags = find->i.flags;
  556. #ifdef MemCheck_MEMCHECK
  557.       MemCheck_UnRegisterMiscBlock(find->i.ibarpatch_block);
  558. #endif
  559.       update_icon(task, &find->i);
  560.       break;
  561.     }
  562.     } break;
  563.  
  564.     /* Remove unknown events to stop them wasting our time. */
  565.   default:
  566.     remove = 1;
  567.     break;
  568.   }
  569.   x_catch(error) {
  570.     dscape_task_report_error(error->errmess);
  571.   }
  572.   return remove;
  573. }
  574.  
  575. /* Keep IconbarPatch up-to-date about which icon the pointer is over so that
  576.    it can return modified info. */
  577. static bool null_event_handler(const wimp_block *event,
  578.     const toolbox_block *ids, void *xhandle)
  579. {
  580.   iconbar_manager_task *task = xhandle;
  581.   iconbar_manager_icon *icon;
  582.   LOGFUNC(null_event_handler);
  583.  
  584.   /* Tell IconbarPatch which icon handle the pointer is over. */
  585.   icon = icon_from_pointer(task, 0, 0, 0);
  586.   if(icon) {
  587.     /* Pointer is over icon, so set details. */
  588.     wimp_w wimp_window = window_get_wimp_handle(0,
  589.     iconbar_gadget_get_window(icon->gadget));
  590.     wimp_window_state state;
  591.  
  592.     /* Copy window state (for Wimp_GetWindowState on iconbar). */
  593.     state.w = wimp_window;
  594.     wimp_get_window_state(&state);
  595.     task->module->iconbar_state = state;
  596.  
  597.     /* Copy rest of data. */
  598.     task->module->window = wimp_window;
  599.     task->module->pointer_icon = icon->icon_handle;
  600.     task->module->pointer_task = icon->task;
  601.   }
  602.   else if(task->module->window) {
  603.     /* The pointer is not over an icon, but do we have to unset details? */
  604.     wimp_t owning_task;
  605.     wimp_w owning_window = task->module->window;
  606.  
  607.     /* Find out which task owns the current window. */
  608.     wimp_message msg;
  609.     msg.size = 5*4;
  610.     msg.my_ref = msg.your_ref = 0;
  611.     msg.action = -1;
  612.     /* Unset this so that message gets through to Wimp. */
  613.     task->module->window = 0;
  614.     if(!owning_window ||
  615.       xwimp_send_message_to_window(wimp_USER_MESSAGE_ACKNOWLEDGE,
  616.     &msg, owning_window, -1, &owning_task))
  617.         owning_task = 0;
  618.  
  619.     /* If that task was us, unset details. */
  620.     if(owning_task == dscape_task_get_handle()) {
  621.       task->module->window = 0;
  622.       task->module->pointer_icon = -1;
  623.       task->module->pointer_task = 0;
  624.     }
  625.     else task->module->window = owning_window; /* Set it back again */
  626.   }
  627.  
  628.   return 0; /* Pass event on */
  629. }
  630.  
  631. /* When a task quits, this removes all icons on the iconbar owned by it. */
  632. static bool task_quitting_handler(const wimp_message *message, void *xhandle)
  633. {
  634.   iconbar_manager_task *task = xhandle;
  635.   iconbar_manager_node **find;
  636.   LOGFUNC(task_quitting_handler);
  637.  
  638.   /* Search has to be restarted because list may be reshuffled. */
  639. restart:
  640.   for(find = &task->icons; *find; find = &(*find)->next)
  641.     if((*find)->i.task == message->sender)
  642.       { remove_icon(task, find); goto restart; }
  643.  
  644.   return 0; /* Pass event on */
  645. }
  646.  
  647.  
  648. /* Talk to the icon arranger component */
  649.  
  650. /* Add an icon. */
  651. static void add_icon(iconbar_manager_task *task, ibarpatch_add_info *i,
  652.     const char *task_name)
  653. {
  654.   iconbar_manager_node *node;
  655.   iconbar_manager_icon *icon;
  656.   iconbar_gadget *gadget;
  657.   ibarpatch_icon_properties *icon_info;
  658.   iconbar_arranger_add_info add_info;
  659.   LOGFUNC(add_icon);
  660.  
  661.   /* Create new list node. */
  662.   node = malloc(sizeof(iconbar_manager_node));
  663.   if(!node) x_throw_message(x_msg_memory());
  664.   icon = &node->i;
  665.  
  666.   /* Copy across the simple details. */
  667.   icon->icon_handle = i->icon_handle;
  668.   icon->task = i->task;
  669.   icon->flags = i->flags;
  670.   icon->data = i->data;
  671.   /* Set last update time. */
  672.   icon->update_poll_id = task->poll_id;
  673.   /* Keep block address (allows later updates). */
  674.   icon->ibarpatch_block = i;
  675.  
  676.   /* Convert the icon's priority, and see if it goes next to another icon.
  677.      Fill out the add_info block. */
  678.   if((i->position == wimp_ICON_BAR_LEFT_RELATIVE ||
  679.       i->position == wimp_ICON_BAR_RIGHT_RELATIVE) &&
  680.       i->extra.priority != -1 /* Means extreme left/right */ ) {
  681.     /* The app wants its icon placed next to another icon. */
  682.     iconbar_manager_node *find;
  683.     for(find = task->icons; find; find = find->next) {
  684.       if(i->extra.next_to == find->i.icon_handle) {
  685.         /* Our new icon inherits the priority of the other icon. */
  686.         add_info.priority = find->i.priority;
  687.         add_info.tend_higher =
  688.         (i->position == wimp_ICON_BAR_RIGHT_RELATIVE);
  689.         add_info.next_to = find->i.external_handle;
  690.         /* Copy these details back to record (useful later). */
  691.         if(find->i.priority >= 0) {
  692.           i->position = wimp_ICON_BAR_RIGHT_LOW_PRIORITY;
  693.           i->extra.priority = (find->i.priority - 0x80000) * 0x10000;
  694.         }
  695.         else {
  696.           i->position = wimp_ICON_BAR_LEFT_LOW_PRIORITY;
  697.           i->extra.priority = (-find->i.priority - 0x80000) * 0x10000;
  698.         }
  699.         break;
  700.       }
  701.     }
  702.     /* If the other icon wasn't found, give this one a default position. */
  703.     if(!find) {
  704.       i->position = wimp_ICON_BAR_RIGHT;
  705.       goto by_priority;
  706.     }
  707.   }
  708.   else {
  709.     /* The app has specified a side (and maybe a priority). */
  710.     bool left_side;
  711.     int priority;
  712.   by_priority:
  713.     priority = i->extra.priority;
  714.  
  715.     /* Which side shall it go on? */
  716.     left_side =
  717.         i->position == wimp_ICON_BAR_LEFT ||
  718.         i->position == wimp_ICON_BAR_LEFT_RELATIVE /* Not a mistake */ ||
  719.     i->position == wimp_ICON_BAR_LEFT_HIGH_PRIORITY ||
  720.     i->position == wimp_ICON_BAR_LEFT_LOW_PRIORITY;
  721.  
  722.     /* Handle case where icon goes on extreme left or right. */
  723.     if(i->extra.next_to == -1) { priority = 0x78000000; }
  724.     /* Set priority to zero in simple case. */
  725.     if(i->position == wimp_ICON_BAR_LEFT ||
  726.        i->position == wimp_ICON_BAR_RIGHT) { priority = 0; }
  727.  
  728.     /* Depending on the scan direction, some icons will be to the right of
  729.        other icons with the same priority. */
  730.     add_info.tend_higher =
  731.     i->position == wimp_ICON_BAR_LEFT ||
  732.         i->position == wimp_ICON_BAR_RIGHT_RELATIVE || /* Not a mistake */
  733.         i->position == wimp_ICON_BAR_LEFT_LOW_PRIORITY || /* Left reversed */
  734.         i->position == wimp_ICON_BAR_RIGHT_HIGH_PRIORITY;
  735.  
  736.     /* Now we fudge the original icon priorities into a set of values that
  737.        are easier to compare.  Negative numbers are towards the left,
  738.        positive numbers towards the right. */
  739.     DEBUGF2("Original priorities: %x, %x\n", priority,
  740.     (priority / 0x10000) + 0x80000);
  741.     add_info.priority = (priority / 0x10000) + 0x80000;
  742.     if(left_side) add_info.priority = -add_info.priority;
  743.     add_info.next_to = 0;
  744.   }
  745.   icon->priority = add_info.priority;
  746.   DEBUGF2("Icon priority is %i (%x)\n",
  747.     add_info.priority, add_info.priority);
  748.  
  749.   /* Find icon's details and give them to a newly-spawned gadget. */
  750.   icon_info = ibarpatch_extract_info(icon->task, icon->flags, &icon->data);
  751.   icon->gadget = gadget = iconbar_gadget_create();
  752.   iconbar_gadget_set_sprite(gadget, &icon_info->sprite);
  753.   iconbar_gadget_set_text(gadget, icon_info->text);
  754.   iconbar_gadget_set_task_name(gadget, task_name);
  755.   ibarpatch_free_info(icon_info);
  756.  
  757.   /* Link node into list. */
  758.   node->next = task->icons;
  759.   task->icons = node;
  760.  
  761.   icon->external_handle = task->arranger->add_icon(task->arranger,
  762.                 gadget, &add_info);
  763. }
  764.  
  765. /* Update an icon gadget (icon arranger gets event indirectly). */
  766. static void update_icon(iconbar_manager_task *task,
  767.     iconbar_manager_icon *icon)
  768. {
  769.   iconbar_gadget *gadget = icon->gadget;
  770.   ibarpatch_icon_properties *icon_info;
  771.   LOGFUNC(update_icon);
  772.  
  773.   /* Tell the gadget the new icon info. */
  774.   icon_info = ibarpatch_extract_info(icon->task, icon->flags, &icon->data);
  775.   iconbar_gadget_set_sprite(gadget, &icon_info->sprite);
  776.   iconbar_gadget_set_text(gadget, icon_info->text);
  777.   ibarpatch_free_info(icon_info);
  778.   icon->update_poll_id = task->poll_id;
  779. }
  780.  
  781. /* Remove an icon. */
  782. static void remove_icon(iconbar_manager_task *task,
  783.     iconbar_manager_node **node_ptr)
  784. {
  785.   iconbar_manager_node *node = *node_ptr;
  786.   iconbar_manager_icon *icon = &node->i;
  787.   iconbar_gadget *gadget = icon->gadget;
  788.   int icon_handle = icon->icon_handle;
  789.   LOGFUNC(remove_icon);
  790.  
  791.   task->arranger->remove_icon(task->arranger, gadget, icon->external_handle);
  792.   iconbar_gadget_destroy(gadget);
  793.  
  794.   /* Remove the corresponding add record from IconbarPatch's keep list. */
  795.   {
  796.     ibarpatch_element **rem;
  797. #ifdef MemCheck_MEMCHECK
  798.     /* It is easiest to turn checking off throughout this. */
  799.     MemCheck_SetChecking(0, 0);
  800. #endif
  801.     for(rem = &task->module->list_keep.begin; *rem; rem = &(*rem)->next) {
  802.       if((*rem)->type == ibarpatch_add_type &&
  803.         (*rem)->data.add.icon_handle == icon_handle) {
  804.     /* We have found the matching add event.  Now remove it. */
  805.     ibarpatch_element *old = *rem;
  806.     /* Make sure to handle the special case of this being
  807.        the last in the list. */
  808.     if(task->module->list_keep.end == &(*rem)->next)
  809.       task->module->list_keep.end = rem;
  810.     *rem = (*rem)->next;
  811.     osmodule_free(old);
  812.     --task->module->list_keep.count;
  813.     CHECK_KEEP_LIST(task->module);
  814.     break;
  815.       }
  816.     }
  817. #ifdef MemCheck_MEMCHECK
  818.     MemCheck_SetChecking(1, 1);
  819. #endif
  820.   }
  821.  
  822.   /* If the pointer was over the icon, restore IconbarPatch's values to a
  823.      safer setting.  This prevents interactive help from dying after an
  824.      app is quit. */
  825.   if(task->module->pointer_icon == icon->icon_handle) {
  826.     task->module->window = 0;
  827.     task->module->pointer_icon = -1;
  828.     task->module->pointer_task = 0;
  829.   }
  830.   if(task->pointer_icon == icon) task->pointer_icon = 0;
  831.  
  832.   /* Remove node from list. */
  833.   *node_ptr = node->next;
  834.   free(node);
  835. }
  836.  
  837. /* The arranger's method for telling us that it will no longer be handling
  838.    the given gadget. */
  839. void iconbar_manager_losing_icon(iconbar_manager_task *task,
  840.     iconbar_gadget *gadget)
  841. {
  842.   iconbar_manager_node **node;
  843.   LOGFUNC(losing_icon);
  844.  
  845.   /* Search for the corresponding node in the list and remove it. */
  846.   for(node = &task->icons; *node; node = &(*node)->next)
  847.       if((*node)->i.gadget == gadget) {
  848.     iconbar_manager_node *old = *node;
  849.     *node = old->next;
  850.  
  851.     /* Make pointer values safe. */
  852.     if(task->module->pointer_icon == old->i.icon_handle) {
  853.       task->module->window = 0;
  854.       task->module->pointer_icon = -1;
  855.       task->module->pointer_task = 0;
  856.     }
  857.     if(task->pointer_icon == &old->i) task->pointer_icon = 0;
  858.     /* Free the node. */
  859.     free(old);
  860.     break;
  861.   }
  862.   /* Destroy the gadget. */
  863.   iconbar_gadget_destroy(gadget);
  864. }
  865.