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

  1.  
  2. /* iconbar.c */
  3.  
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include "swis.h"
  7. #include "OS:osword.h"
  8. #include "OS:wimp.h"
  9. #include "OS:toolbox.h"
  10. #include "OS:menu.h"
  11. #include "OS:window.h"
  12. #include "Dreamscape:tboxevent.h"
  13. #include "Dreamscape:wimpevent.h"
  14. #include "Dreamscape:wimpmsg.h"
  15. #include "Dreamscape:task.h"
  16. #include "Dreamscape:debug.h"
  17. #include "Dreamscape:x.h"
  18.  
  19. #ifdef MemCheck_MEMCHECK
  20. #include "MemCheck:memcheck.h"
  21. #endif
  22.  
  23. #include "iconbar.h"
  24. #include "hotkeys.h"
  25. #include "debug.h"
  26. #include "options.h"
  27. #include "various.h"
  28.  
  29.  
  30. /* Private structures */
  31.  
  32. typedef struct thiniconbar_node thiniconbar_node;
  33. struct thiniconbar_node {
  34.   thiniconbar_node *prev, *next;
  35.   thin_iconbar *parent;
  36.  
  37.   enum { node_ICON = 1, node_MIDDLE = 2, node_END = 3 } type;
  38.   iconbar_gadget *gadget;
  39.   int priority;
  40. };
  41. #define VALIDATE_NODE(node)    \
  42.     assert((node)->type >= node_ICON && (node)->type <= node_END)
  43.  
  44. struct thin_iconbar {
  45.   toolbox_o window,
  46.         menu; /* May be 0. */
  47.  
  48.   /* Bi-directional linked list. */
  49.   thiniconbar_node ends, middle;
  50.  
  51.   unsigned mode_changed: 1;    /* Trigger update after a mode change. */
  52.   int total_width;        /* Total width of icons (excl. middle). */
  53.   int middle_width;        /* Width of middle gap at last update. */
  54.  
  55.   /* Scrolling information. */
  56.   unsigned scroll_state: 1;   /* Whether currently scrolling (types 2/3). */
  57.   unsigned scrolled_last: 1;  /* Whether scrolled on last null event. */
  58.   unsigned last_null;         /* Time of last null event. */
  59.  
  60.   /* It is useful to cache screen info. */
  61.   int screen_width, screen_height;
  62.   short screen_xeig, screen_yeig;
  63.  
  64.   iconbar_arranger arranger;
  65. };
  66.  
  67.  
  68. /* Internal function prototypes */
  69.  
  70. #define MODULE_NAME    "thiniconbar"
  71.  
  72. /* Wimp events */
  73. static bool click_handler(const wimp_block *event,
  74.     const toolbox_block *ids, void *handle);
  75. static bool key_handler(const wimp_block *event,
  76.     const toolbox_block *ids, void *handle);
  77. static bool null_event_handler(const wimp_block *event,
  78.     const toolbox_block *ids, void *handle);
  79.  
  80. /* Wimp messages */
  81. static bool mode_change_handler(const wimp_message *message, void *handle);
  82.  
  83. static bool icon_resize_handler(iconbar_gadget *gadget,
  84.     os_box *old, const os_coord *new_size, void *handle);
  85.  
  86. static int find_x_extent(const thin_iconbar *obj);
  87. static int find_middle_width(const thin_iconbar *obj);
  88. static void set_pointer_bounds(const os_box *box);
  89. static void reopen_window(thin_iconbar *obj);
  90. static void read_screen_info(thin_iconbar *obj);
  91.  
  92. static void *add_icon(iconbar_arranger *arranger, iconbar_gadget *gadget,
  93.     const iconbar_arranger_add_info *info);
  94. static void remove_icon(iconbar_arranger *arranger, iconbar_gadget *gadget,
  95.     void *handle);
  96.  
  97.  
  98. /* Initialisation */
  99.  
  100. thin_iconbar *thiniconbar_create(void)
  101. {
  102.   thin_iconbar *obj;
  103.   wimp_w our_window;
  104.   LOGFUNC(create);
  105.  
  106.   obj = malloc(sizeof(thin_iconbar));
  107.   if(!obj) x_throw_message(x_msg_memory());
  108.   dscape_task_ensure();
  109.  
  110.   /* Create the iconbar window, changing the background colour. */
  111. #if 1
  112.   {
  113.     /* It is not documented whether I can modify a template returned by
  114.        the Toolbox.  I assume I cannot.  However, copying the template just
  115.        to be safe doesn't work for some reason (change not made).  So I'll
  116.        just make sure to return the template to its original state. */
  117.     int old_work_bg, old_title_fg;
  118.     toolbox_id t;
  119.     window_window *block;
  120.     /* Look up template. */
  121.     t = toolbox_template_look_up(0, "Iconbar");
  122. #ifdef MemCheck_MEMCHECK
  123.     MemCheck_RegisterMiscBlock(t, 200);
  124. #endif
  125.     block = &((window_object *) ((toolbox_resource_file_object *) t)->
  126.     object)->window;
  127.     /* Change values, remembering originals. */
  128.     old_work_bg = block->work_bg;
  129.     old_title_fg = block->title_fg;
  130.     block->work_bg = options->window_bg_colour;
  131.     block->title_fg = options->window_border ?
  132.         wimp_COLOUR_BLACK : wimp_COLOUR_TRANSPARENT;
  133.     /* Create object. */
  134.     obj->window = toolbox_create_object(toolbox_CREATE_GIVEN_OBJECT, t);
  135.     /* Change values back and finish. */
  136.     block->work_bg = old_work_bg;
  137.     block->title_fg = old_title_fg;
  138. #ifdef MemCheck_MEMCHECK
  139.     MemCheck_UnRegisterMiscBlock(t);
  140. #endif
  141.   }
  142. #else
  143.   obj->window = toolbox_create_object(0, (toolbox_id) "Iconbar");
  144. #endif
  145.   our_window = window_get_wimp_handle(0, obj->window);
  146.  
  147.   /* Initialise function table of base class. */
  148.   obj->arranger.add_icon = add_icon;
  149.   obj->arranger.remove_icon = remove_icon;
  150.  
  151.   /* Initialise icon list. */
  152.   /* End node (list head). */
  153.   obj->ends.next = obj->ends.prev = &obj->middle;
  154.   obj->ends.parent = obj;
  155.   obj->ends.type = node_END;
  156.   obj->ends.gadget = 0;
  157.   obj->ends.priority = 0;
  158.   /* Middle node (separates left and right sides). */
  159.   obj->middle.next = obj->middle.prev = &obj->ends;
  160.   obj->middle.parent = obj;
  161.   obj->middle.type = node_MIDDLE;
  162.   obj->middle.gadget = 0;
  163.   obj->middle.priority = 0;
  164.  
  165.   /* Set variables to initial values */
  166.   obj->menu = 0; /* Menu is given to us later. */
  167.   obj->total_width = 0;
  168.   obj->middle_width = 0; /* Will be filled in by resize handler later. */
  169.   /* Scrolling info. */
  170.   obj->scroll_state = 0;
  171.   obj->scrolled_last = 0;
  172.   obj->last_null = os_read_monotonic_time();
  173.  
  174.   /* Register our Wimp event handlers */
  175.   dscape_wimpevent_register_handler(wimp_MOUSE_CLICK,
  176.     our_window, click_handler, obj);
  177.   dscape_wimpevent_register_handler(wimp_NULL_REASON_CODE,
  178.     0, null_event_handler, obj);
  179.   /* Register a handler for Shift-F12 keypresses */
  180.   hotkeys_initialise();
  181.   dscape_wimpevent_register_handler(wimp_KEY_PRESSED, 0,
  182.     key_handler, obj);
  183.  
  184.   /* Register our Wimp message handlers */
  185.   dscape_wimpmsg_register_handler(message_MODE_CHANGE,
  186.     mode_change_handler, obj);
  187.  
  188.   /* Set up the window correctly */
  189.   read_screen_info(obj);
  190.   reopen_window(obj);
  191.  
  192.   /* Tell the world the iconbar height. */
  193.   setenv_int("Iconbar$Height", options->window_height);
  194.  
  195.   return obj;
  196. }
  197.  
  198. void thiniconbar_destroy(thin_iconbar *obj)
  199. {
  200.   thiniconbar_node *node;
  201.   wimp_w our_window = window_get_wimp_handle(0, obj->window);
  202.   LOGFUNC(destroy);
  203.  
  204.   /* Remove all icons from the window (doesn't do it properly yet). */
  205.   for(node = obj->ends.next; node->type != node_END; ) {
  206.     VALIDATE_NODE(node);
  207.     if(node->type == node_ICON) {
  208.       thiniconbar_node *old = node;
  209.       node = node->next;
  210.  
  211.       /* Tell the icon manager we're ending control of the icon. */
  212.       iconbar_manager_losing_icon(iconbar_manager_this_task, old->gadget);
  213.       /* We don't need any other finalisation for the icon. */
  214.       free(old);
  215.     }
  216.     else {
  217.       /* Skip other node types. */
  218.       node = node->next;
  219.     }
  220.   }
  221.  
  222.   /* Deregister our Wimp message handlers */
  223.   dscape_wimpmsg_deregister_handler(message_MODE_CHANGE,
  224.     mode_change_handler, obj);
  225.  
  226.   /* Deregister our Wimp event handlers */
  227.   dscape_wimpevent_deregister_handler(wimp_MOUSE_CLICK,
  228.     our_window, click_handler, obj);
  229.   dscape_wimpevent_deregister_handler(wimp_NULL_REASON_CODE,
  230.     0, null_event_handler, obj);
  231.   dscape_wimpevent_deregister_handler(wimp_KEY_PRESSED, 0,
  232.     key_handler, obj);
  233.  
  234.   /* Destroy our Toolbox objects. */
  235.   toolbox_delete_object(0, obj->window);
  236.  
  237.   /* Revoke iconbar height. */
  238.   unsetenv("Iconbar$Height");
  239.  
  240.   free(obj);
  241. }
  242.  
  243.  
  244. /* Inheritance casting */
  245.  
  246. #ifndef offsetof
  247. #define offsetof(str, comp)    \
  248.     ((char *) &((str *) 0)->comp - (char *) ((str *) 0))
  249. #endif
  250.  
  251. thin_iconbar *thiniconbar_castfrom_arranger(iconbar_arranger *arranger)
  252. {
  253.   if(!arranger) return 0;
  254.   return (thin_iconbar *) ((char *) arranger -
  255.     offsetof(thin_iconbar, arranger));
  256. }
  257.  
  258. iconbar_arranger *thiniconbar_castto_arranger(thin_iconbar *obj)
  259. {
  260.   if(!obj) return 0;
  261.   return &obj->arranger;
  262. }
  263.  
  264.  
  265. /* Methods */
  266.  
  267. void thiniconbar_set_menu(thin_iconbar *obj, toolbox_o menu)
  268. {
  269.   LOGFUNC(set_menu);
  270.   if(!obj) return;
  271.   obj->menu = menu;
  272. }
  273.  
  274.  
  275. /* Internal functions */
  276.  
  277. /* Returns the horizontal extent of the iconbar window. */
  278. static int find_x_extent(const thin_iconbar *obj)
  279. {
  280.   int icons_width;
  281.   LOGFUNC(find_x_extent);
  282.  
  283.   icons_width = obj->total_width + (options->end_gap * 2) +
  284.     options->central_gap - (options->between_gap * 2);
  285.   return icons_width > obj->screen_width ?
  286.     icons_width : obj->screen_width;
  287. }
  288.  
  289. /* Returns the width of the gap between left and right hand sides. */
  290. static int find_middle_width(const thin_iconbar *obj)
  291. {
  292.   int rest;
  293.   LOGFUNC(find_middle_width);
  294.  
  295.   rest = obj->total_width + (options->end_gap * 2) -
  296.     (options->between_gap * 2);
  297.   return (rest + options->central_gap) > obj->screen_width ?
  298.     options->central_gap :
  299.     obj->screen_width - rest;
  300. }
  301.  
  302. /* Resets the window's extent and re-opens it in the correct position at the
  303.    bottom of the screen.  read_screen_info() must have been called recently
  304.    to cache mode info. */
  305. static void reopen_window(thin_iconbar *obj)
  306. {
  307.   wimp_window_state state;
  308.   os_box extent;
  309.   LOGFUNC(reopen_window);
  310.  
  311.   /* Set the extent of the window appropriately. */
  312.   extent.x0 = 0;
  313.   extent.y0 = -options->window_height;
  314.   extent.x1 = find_x_extent(obj);
  315.   extent.y1 = 0;
  316.   window_set_extent(0, obj->window, &extent);
  317.  
  318.   /* Get the window state. */
  319.   state.w = window_get_wimp_handle(0, obj->window);
  320.   wimp_get_window_state(&state);
  321.   /* If the window is not open currently, make sure it is opened at the
  322.      bottom of the window stack.  This prevents another case of window
  323.      disappearance on a mode change. */
  324.   if(!(state.flags & wimp_WINDOW_OPEN)) state.next = wimp_BOTTOM;
  325.  
  326.   /* Set the window's extent. */
  327.   state.visible.x0 = 0;
  328.   state.visible.y0 = 0;
  329.   state.visible.x1 = obj->screen_width;
  330.   state.visible.y1 = options->window_height;
  331.  
  332.   /* And re-open the iconbar! */
  333.   toolbox_show_object(0, obj->window, toolbox_POSITION_FULL,
  334.     (toolbox_position *) &state.visible, 0, -1);
  335.  
  336.   /* Cancel pending refresh even if the pending refresh
  337.      did not cause this call. */
  338.   obj->mode_changed = 0;
  339. }
  340.  
  341.  
  342. /* Event handlers */
  343.  
  344. /* Unfortunately we can't mask this out when the pointer leaves our window,
  345.    because the Wimp stops giving entering/leaving events when a drag is in
  346.    progress, which is useless for our purposes.
  347. */
  348. static bool null_event_handler(const wimp_block *event,
  349.     const toolbox_block *ids, void *xhandle)
  350. {
  351.   thin_iconbar *obj = xhandle;
  352.   bool did_scroll = 0;
  353.   int this_time = os_read_monotonic_time();
  354.   wimp_pointer ptr;
  355.   wimp_w our_window = window_get_wimp_handle(0, obj->window);
  356.   LOGFUNC(null_event_handler);
  357.  
  358.   /* Get the mouse's state.  We have to stop IconbarPatch from fiddling the
  359.      info returned, otherwise we can't tell it how to fiddle it for other
  360.      tasks!  (And I wondered why it wasn't working properly...) */
  361.   iconbar_manager_get_pointer_info(iconbar_manager_this_task, &ptr);
  362.  
  363.   /* Scroll the iconbar if the pointer is over our window or has gone
  364.      off-screen to scroll it, but only if scrolling is enabled. */
  365.   if(options->scroll_type && (ptr.w == our_window || obj->scroll_state)) {
  366.     wimp_window_state state;
  367.     int scroll_by = 0;
  368.  
  369.     /* Time difference between scrolls, in centiseconds. */
  370.     int between_time = this_time - obj->last_null;
  371.     if(!obj->scrolled_last) between_time = (between_time + 1) / 2;
  372.     if(between_time <= 0) between_time = 1;
  373.  
  374.     state.w = our_window;
  375.     wimp_get_window_state(&state);
  376.  
  377.     switch(options->scroll_type) {
  378.  
  379.     /* Do fixed speed scrolling. */
  380.     case scroll_type_LINEAR:
  381.     case scroll_type_LINEAR_S: {
  382.       /* If the pointer is the margins of the window, do scroll. */
  383.       if(ptr.pos.x < (state.visible.x0 + options->scroll_margin))
  384.     { scroll_by = -1; did_scroll = 1; }
  385.       else if(ptr.pos.x >= (state.visible.x1 - options->scroll_margin))
  386.     { scroll_by = 1; did_scroll = 1; }
  387.       /* Find the scroll step in OS units. */
  388.       if(scroll_by && options->scroll_type == scroll_type_LINEAR)
  389.         scroll_by *= options->scroll_step;
  390.         else scroll_by = scroll_by * options->scroll_speed *
  391.         between_time;
  392.     } break;
  393.  
  394.     /* Do dynamic speed scrolling (pointer goes off-screen). */
  395.     case scroll_type_PROP:
  396.     case scroll_type_MOVE: {
  397.       os_box box;
  398.       /* If we were scrolling, but the pointer moved away, reset its box. */
  399.       if(obj->scroll_state && (ptr.pos.y < state.visible.y0 ||
  400.                 ptr.pos.y >= state.visible.y1)) {
  401.         box.x0 = box.y0 = 0;
  402.         box.x1 = obj->screen_width - (1 << obj->screen_xeig);
  403.         box.y1 = obj->screen_height - (1 << obj->screen_yeig);
  404.         set_pointer_bounds(&box);
  405.         obj->scroll_state = 0;
  406.         goto scroll_end; /* Don't scroll now. */
  407.       }
  408.       /* Set the pointer bounds to allow it off-screen. */
  409.       box.y0 = 0;
  410.       box.y1 = obj->screen_height - (1 << obj->screen_yeig);
  411.       box.x0 = state.visible.x0 - state.xscroll;
  412.       box.x1 = state.visible.x0 - state.xscroll + find_x_extent(obj) -
  413.         (1 << obj->screen_xeig);
  414.       set_pointer_bounds(&box);
  415.       obj->scroll_state = 1;
  416.  
  417.       /* Scroll according to distance off-screen. */
  418.       if(ptr.pos.x < state.visible.x0)
  419.     { scroll_by = ptr.pos.x - state.visible.x0; did_scroll = 1; }
  420.       else if(ptr.pos.x >= (state.visible.x1 - (1 << obj->screen_xeig)))
  421.         { scroll_by = ptr.pos.x - state.visible.x1 +
  422.             (1 << obj->screen_xeig); did_scroll = 1; }
  423.     } break;
  424.     }
  425.  
  426.     /* Scroll the window as instructed. */
  427.     if(scroll_by) {
  428.       DEBUGF1("Scrolling window by %i\n", scroll_by);
  429.       state.xscroll += scroll_by;
  430.       wimp_open_window((wimp_open *) &state);
  431.  
  432.       /* Move the pointer with the window as well (type 3 only). */
  433.       if(options->scroll_type == scroll_type_MOVE) {
  434.         int x = ptr.pos.x - scroll_by,
  435.         y = ptr.pos.y;
  436.  
  437.         /* oswordpointer_position_block is also broken.  D'uh! */
  438.         struct {
  439.           unsigned char reason, coords[4];
  440.         } b;
  441.         b.reason = 3; /* Move pointer, sub reason code */
  442.         b.coords[0] = x & 0xFF;
  443.         b.coords[1] = (x >> 8) & 0xFF;
  444.         b.coords[2] = y & 0xFF;
  445.         b.coords[3] = (y >> 8) & 0xFF;
  446.         /* OSLib function broken. */
  447.         /* oswordpointer_set_position(
  448.         (oswordpointer_position_block *) &b); */
  449.         _swi(OS_Word, _IN(0)|_IN(1), 21 /* Reason code */, &b);
  450.       }
  451.     }
  452.   scroll_end: ;
  453.   }
  454.   /* Ongoing info for scrolling. */
  455.   obj->scrolled_last = did_scroll;
  456.   obj->last_null = this_time;
  457.  
  458.   /* Update the window if a mode change occurred. */
  459.   if(obj->mode_changed) {
  460.     /* Iconbar's middle gap may need resizing. */
  461.     os_box bbox;
  462.     os_coord size;
  463.     DEBUGF("Handling mode change flag\n");
  464.     bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
  465.     size.x = size.y = 0;
  466.     icon_resize_handler(0, &bbox, &size, &obj->middle);
  467.     reopen_window(obj); /* Unsets mode changed flag */
  468.   }
  469.  
  470.   return 0;
  471. }
  472.  
  473. /* Sets the boundaries of the pointer. */
  474. static void set_pointer_bounds(const os_box *box)
  475. {
  476.   /* oswordpointer_bbox_block appears to be broken.
  477.      We must define our own structure. */
  478.   struct {
  479.     unsigned char reason, bbox[8];
  480.   } b;
  481.   b.reason = 1; /* Set bbox, sub reason code */
  482.   b.bbox[0] = box->x0 & 0xFF;
  483.   b.bbox[1] = (box->x0 >> 8) & 0xFF;
  484.   b.bbox[2] = box->y0 & 0xFF;
  485.   b.bbox[3] = (box->y0 >> 8) & 0xFF;
  486.   b.bbox[4] = box->x1 & 0xFF;
  487.   b.bbox[5] = (box->x1 >> 8) & 0xFF;
  488.   b.bbox[6] = box->y1 & 0xFF;
  489.   b.bbox[7] = (box->y1 >> 8) & 0xFF;
  490.   /* OSLib's function also seems to be broken. */
  491.   /* oswordpointer_set_bbox((oswordpointer_bbox_block *) &b); */
  492.   _swi(OS_Word, _IN(0)|_IN(1), 21 /* Reason code */, &b);
  493. }
  494.  
  495. static bool click_handler(const wimp_block *event,
  496.     const toolbox_block *ids, void *xhandle)
  497. {
  498.   thin_iconbar *obj = xhandle;
  499.   LOGFUNC(click_handler);
  500.  
  501.   /* If Menu was clicked on an empty area, open our menu (but only if one
  502.      has been attached).  If Menu was clicked right at the bottom of the
  503.      screen, ignore it:  this ensures the extension module `Toggle' still
  504.      works. */
  505.   if(event->pointer.buttons == 2 && event->pointer.pos.y != 0 && obj->menu) {
  506.     wimp_window_state state;
  507.     os_coord top_left;
  508.  
  509.     state.w = window_get_wimp_handle(0, obj->window);
  510.     wimp_get_window_state(&state);
  511.  
  512.     top_left.x = event->pointer.pos.x - 64;
  513.     top_left.y = state.visible.y1 - options->top_gap -
  514.     options->menu_y_offset + menu_get_height(0, obj->menu);
  515.     toolbox_show_object(0, obj->menu, toolbox_POSITION_TOP_LEFT,
  516.     (toolbox_position *) &top_left, obj->window, -1);
  517.     return 1; /* Claim event */
  518.   }
  519.   return 0; /* Pass event on */
  520. }
  521.  
  522. static bool key_handler(const wimp_block *event,
  523.     const toolbox_block *ids, void *xhandle)
  524. {
  525.   thin_iconbar *obj = xhandle;
  526.   LOGFUNC(key_handler);
  527.  
  528.   /* So, was Shift-F12 pressed? */
  529.   if(event->key.c == 0x1DC) {
  530.     thiniconbar_toggle_front(obj);
  531.     return 1;
  532.   }
  533.  
  534.   /* Pass the key on to another task */
  535.   wimp_process_key(event->key.c);
  536.   return 1;
  537. }
  538.  
  539. /* Move iconbar to the front if obscured, or to the back if not.  Will also
  540.    change the `backdrop' flag:  this avoids a nasty bug where windows get
  541.    hidden behind the Pinboard on a mode change. */
  542. void thiniconbar_toggle_front(thin_iconbar *obj)
  543. {
  544.   wimp_window_state state;
  545.   wimp_window_flags set_flags;
  546.   LOGFUNC(toggle_front);
  547.  
  548.   state.w = window_get_wimp_handle(0, obj->window);
  549.   wimp_get_window_state(&state);
  550.  
  551.   if(state.flags & wimp_WINDOW_NOT_COVERED) {
  552.     /* Iconbar is not covered:  send to back, set `backdrop' flag. */
  553.     state.next = wimp_BOTTOM;
  554.     set_flags = wimp_WINDOW_BACK;
  555.   }
  556.   else {
  557.     /* Iconbar is covered:  put to front, unset `backdrop' flag. */
  558.     state.next = wimp_TOP;
  559.     set_flags = 0;
  560.   }
  561.   /* Flags should be changed before the window is re-opened. */
  562.   set_window_flags(state.w, set_flags, wimp_WINDOW_BACK);
  563.   wimp_open_window((wimp_open *) &state);
  564. }
  565.  
  566. /* Update after a mode change has taken place. */
  567. static bool mode_change_handler(const wimp_message *message, void *xhandle)
  568. {
  569.   thin_iconbar *obj = xhandle;
  570.   LOGFUNC(mode_change_handler);
  571.  
  572.   /* This is called after the actual screen mode change has taken place.
  573.      Screen info can be read now therefore, but resizing the window has to
  574.      be delayed slightly (IIRC). */
  575.   read_screen_info(obj);
  576.   obj->mode_changed = 1;
  577.  
  578.   return 0; /* Pass event on */
  579. }
  580.  
  581. /* Cache info about the current screen mode. */
  582. static void read_screen_info(thin_iconbar *obj)
  583. {
  584.   int eig, size;
  585.   LOGFUNC(read_screen_info);
  586.  
  587.   /* Work out how wide the screen is. */
  588.   os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR, &eig);
  589.   os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XWIND_LIMIT, &size);
  590.   obj->screen_xeig = eig;
  591.   obj->screen_width = (size + 1) << eig;
  592.  
  593.   /* And the height too. */
  594.   os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR, &eig);
  595.   os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YWIND_LIMIT, &size);
  596.   obj->screen_yeig = eig;
  597.   obj->screen_height = (size + 1) << eig;
  598. }
  599.  
  600. static void *add_icon(iconbar_arranger *arranger, iconbar_gadget *gadget,
  601.     const iconbar_arranger_add_info *info)
  602. {
  603.   thin_iconbar *obj = thiniconbar_castfrom_arranger(arranger);
  604.   thiniconbar_node *node, *ins;
  605.   os_box bbox;
  606.   os_coord size;
  607.   LOGFUNC(add_icon);
  608.  
  609.   /* Create new node. */
  610.   node = malloc(sizeof(thiniconbar_node));
  611.   if(!node) x_throw_message(x_msg_memory());
  612.   /* Fill in the node's fields. */
  613.   node->type = node_ICON;
  614.   node->parent = obj;
  615.   node->gadget = gadget;
  616.   node->priority = info->priority;
  617.  
  618.   /* Search through the list to find correct place for new icon. */
  619.   /* May search through left or right hand lists. */
  620.   ins = (info->priority >= 0 ?
  621.     obj->middle.next : /* Right side */
  622.     obj->ends.next);   /* Left side */
  623.   if(info->next_to) {
  624.     /* Search for the icon that this one goes next to. */
  625.     for(; ins->type != node_END; ins = ins->next) {
  626.       VALIDATE_NODE(ins);
  627.       if(ins->type == node_ICON && ins == info->next_to) {
  628.         /* Move on one if this icon goes to the right of the other icon. */
  629.         if(info->tend_higher && ins != &obj->ends) ins = ins->next;
  630.         goto found;
  631.       }
  632.     }
  633.     /* Icon wasn't found so resort to searching by priority. */
  634.     goto by_priority;
  635.   }
  636.   else {
  637.   by_priority:
  638.     /* Look for icons with matching priority. */
  639.     for(; ins->type != node_END; ins = ins->next) {
  640.       VALIDATE_NODE(ins);
  641.       if(ins->type == node_ICON && info->tend_higher ?
  642.         ins->priority > info->priority :
  643.         ins->priority >= info->priority) goto found;
  644.     }
  645.     /* Otherwise insert at the end. */
  646.   }
  647. found:
  648.  
  649.   /* Link node into list, before the node ins (the one we just found). */
  650.   node->next = ins;
  651.   node->prev = ins->prev;
  652.   ins->prev->next = node;
  653.   ins->prev = node;
  654.  
  655.   iconbar_gadget_get_bbox(gadget, &bbox);
  656.   size.x = bbox.x1 - bbox.x0;
  657.   size.y = options->icon_height;
  658.  
  659.   /* We have to fake a resize event so that other icons are shifted.
  660.      Give a zero width to start with.  Work out where the icon goes. */
  661.   {
  662.     int pos = 0;
  663.     thiniconbar_node *prev = node->prev;
  664.  
  665.     /* Take into account the middle gap for icons on the right. */
  666.     if(node->priority >= 0) {
  667.       /* The width of the middle gap must be worked out for *after* the icon
  668.          is added.  Change the total width only temporarily, because the
  669.          resize handler will update it permanently. */
  670.       int new_width;
  671.       obj->total_width += size.x + options->between_gap;
  672.       new_width = find_middle_width(obj) - options->between_gap;
  673.       obj->total_width -= size.x + options->between_gap;
  674.  
  675.       if(prev->type == node_MIDDLE) {
  676.         pos += new_width;
  677.         prev = prev->prev;
  678.       }
  679.       else {
  680.         pos += new_width - obj->middle_width + options->between_gap;
  681.       }
  682.     }
  683.  
  684.     /* Move on after the middle gap. */
  685.     if(prev->type == node_END) {
  686.       pos += options->end_gap;
  687.     }
  688.     else { /* prev->type == node_ICON */
  689.       os_box other_bbox;
  690.       iconbar_gadget_get_bbox(prev->gadget, &other_bbox);
  691.       pos += other_bbox.x1 + options->between_gap;
  692.     }
  693.  
  694.     bbox.x0 = bbox.x1 = pos;
  695.   }
  696.   bbox.y1 = -options->top_gap;
  697.   bbox.y0 = -options->top_gap - options->icon_height;
  698.   icon_resize_handler(gadget, &bbox, &size, node);
  699.   iconbar_gadget_set_bbox(gadget, &bbox);
  700.  
  701.   iconbar_gadget_set_resize_handler(gadget, icon_resize_handler, node);
  702.   iconbar_gadget_set_window(gadget, obj->window);
  703.  
  704.   return node;
  705. }
  706.  
  707. static void remove_icon(iconbar_arranger *arranger, iconbar_gadget *gadget,
  708.     void *handle)
  709. {
  710.   thin_iconbar *obj = thiniconbar_castfrom_arranger(arranger);
  711.   thiniconbar_node *node = handle, *next, *prev;
  712.   LOGFUNC(remove_icon);
  713.  
  714.   /* Detach from this window. */
  715.   iconbar_gadget_set_window(gadget, 0);
  716.  
  717.   /* Fake another resize event so that other icons are shifted back. */
  718.   /* Give the new size as zero. */
  719.   {
  720.     os_box bbox;
  721.     os_coord size;
  722.     iconbar_gadget_get_bbox(gadget, &bbox);
  723.     size.x = 0;
  724.     size.y = bbox.y1 - bbox.y0;
  725.     icon_resize_handler(gadget, &bbox, &size, node);
  726.   }
  727.  
  728.   /* Remove icon from the list. */
  729.   next = node->next;
  730.   prev = node->prev;
  731.   next->prev = prev;
  732.   prev->next = next;
  733.   free(node);
  734. }
  735.  
  736. /* Gives the bounding box of an icon being resized, and shuffles the other
  737.    icons on the iconbar in order to accommodate it. */
  738. static bool icon_resize_handler(iconbar_gadget *gadget,
  739.     os_box *box, const os_coord *new_size, void *handle)
  740. {
  741.   thiniconbar_node *node = handle, *n;
  742.   thin_iconbar *obj = node->parent;
  743.   int old_width = box->x1 - box->x0,
  744.       move = 0;
  745.   LOGFUNC(icon_resize_handler);
  746.  
  747.   /* Update the bounding box of this icon. */
  748.   obj->total_width += new_size->x - old_width;
  749.   box->x1 = box->x0 + new_size->x;
  750.   box->y0 = box->y1 - new_size->y;
  751.  
  752.   /* Handle special cases of adding and removing an icon. */
  753.   if(old_width == 0) obj->total_width += options->between_gap;
  754.   else if(new_size->x == 0) obj->total_width -= options->between_gap;
  755.  
  756.   /* If the icon was on the right, shuffle all icons on the right (since the
  757.      middle gap may also be resized).  Otherwise, only shuffle icons to the
  758.      right of the icon being resized. */
  759.   n = (node->priority >= 0 ? &obj->middle : node);
  760.   while(n->type != node_END) {
  761.     VALIDATE_NODE(n);
  762.     if(n->type == node_ICON && n == node) {
  763.       /* Encountered the icon being resized, so change amount to move by. */
  764.       move += new_size->x - old_width;
  765.       /* Special cases. */
  766.       if(old_width == 0) move += options->between_gap;
  767.       else if(new_size->x == 0) move -= options->between_gap;
  768.     }
  769.     else if(n->type == node_ICON) {
  770.       /* Move an icon (not the one being resized). */
  771.       if(move != 0) {
  772.         os_box b;
  773.         iconbar_gadget_get_bbox(n->gadget, &b);
  774.         b.x0 += move;
  775.         b.x1 += move;
  776.         iconbar_gadget_set_bbox(n->gadget, &b);
  777.       }
  778.     }
  779.     else if(n->type == node_MIDDLE) {
  780.       /* Account for the middle gap changing in width. */
  781.       int new_width = find_middle_width(obj);
  782.       move += new_width - obj->middle_width;
  783.       obj->middle_width = new_width;
  784.     }
  785.     n = n->next;
  786.   }
  787.  
  788.   /* Window might need resizing. */
  789.   reopen_window(obj);
  790.  
  791.   return 0; /* Success */
  792. }
  793.