Xref: utzoo alt.toolkits.xview:126 comp.windows.open-look:561 Path: utzoo!utgpu!cs.utexas.edu!sun-barr!newstop!exodus!frisbee.Eng.Sun.COM!jcb From: jcb@frisbee.Eng.Sun.COM (Jim Becker) Newsgroups: sun.open-windows,alt.toolkits.xview,comp.windows.open-look Subject: Detecting and handling mouse double click with XView - paper Keywords: doubleclick Message-ID: <7698@exodus.Eng.Sun.COM> Date: 8 Feb 91 23:18:00 GMT Sender: news@exodus.Eng.Sun.COM Followup-To: sun.open-windows,alt.toolkits.xview,comp.windows.open-look Lines: 274 Detecting and handling mouse double click with XView by Jim Becker Sun Microsystems, Inc. -- Feb 8, 1991 Are you interested in double click functionality for your XView application? Many programmers are rolling their own double click algorithms for XView based applications. Some questions have been raised concerning this, as little (if any) documentation covers this topic. Thus this paper has been created. This is a primer on how to detect double-click actions by the user within the XView toolkit. And how to structure double click functions semantically. -- double click scenarios -- There are two main strategies for alerting a program of end user double clicking. The first being generation of specific double click events in the event stream. In this scenario the toolkit, specifically the event dispatch logic, would recognize and emit actions significant to double click. Thus, in addition to the ACTION_SELECT events, there would be an ACTION_DBL_SELECT when a double click was detected. It would be possible to add a new action, ACTION_DBL_SELECT, to the XView logic. Or a flag can be added to the Event structure indicating multi-click has taken place. However, one can provide the same functionality using existing field releases of XView. One simply has to add a snippet of code to their logic. In the future we may indeed provide a new action, or augment the Event structure to indicate double click selection; but you can do it yourself *today* in your own home! (Well, workstation.) The second scenario is detecting double click by checking for the delta time between two successive mouse clicks. In the case of XView, as well as Xlib, the second means is the suggested approach - as there are no explicit double click events defined for either event stream. Within XView, double click can be detected by calculating time deltas between subsequent ACTION_SELECT messages. The user event handler has to cache the time value for the last ACTION_SELECT event, and use this time for a delta calculation if another ACTION_SELECT comes in next. If the time between two ACTION_SELECT events is within the multi click timeout, the application program processes the second mouse click as a double click. Extensions of this into triple and quadruple (!) clicks can be done by augmenting the logic. It would be up to the application to provide semantic consistency for this, of course. -- logical use of single/double click functionality -- It is important for the application programmer to understand that a double click action should be an extension of the action taken with a single click. Meaning that the application process the first click without waiting to see if there is another click, or performing a fundamentally different action when there is a double click. For example, a smooth interpretation of the single/double click paridigm would be: Save current file Save all files or Select word Select line Normally, one does not want to create semantic actions where the double click means something fundamentally different from single: Save current file Load new file Some implementations of double click can be created to perform this sort of functionality. Specifically the program can delve into the coming event queue searching for more mouse down events. Or a timer can be used that goes off after the double click threshhold. Although there are sometimes reasons to do this, in the spirit of user interface consistency we hope you refrain from this if possible. If you desire code that performs this function, we have some examples (email me). However this sort of usage is generally discouraged. -- code to test for double click in XView -- Within the code to detect double clicks, the application programmer needs to do several things. The programmer needs to add a few values to the object data content associated with their textsw object. These values are used to store the last time value for the previous ACTION_SELECT event. The X/Y location of this event should also be stored, as it is used to determine the delta of the mouse position between subsequent mouse clicks. Here is the code in current textsw that determines multi-click, for example. This code is from the file txt_sel.c, with a little formatting cleanup. I believe this is representative to how most implementations are done. (it is also somewhat incomplete - look at the source for more detail.) ... switch (event_action(ie)) { case TXTSW_POINT: /* aka ACTION_SELECT */ int delta; /* in millisecs */ if (folio->state & TXTSW_SHIFT_DOWN) folio->track_state |= TXTSW_TRACK_ADJUST; else folio->track_state |= TXTSW_TRACK_POINT; delta = (ie->ie_time.tv_sec - folio->last_point.tv_sec) * 1000; delta += ie->ie_time.tv_usec / 1000; delta -= folio->last_point.tv_usec / 1000; if (delta >= folio->multi_click_timeout) { /* single click */ } else { /* multi-click */ } .... In this case time within the event is compared with the time of the last select event, and if it's within the `multi_click_timeout' range then semantically the multi-click is selected. This same logic can be combined into a singular function that statically retains the information needed to detect the double click. The advantage of this approach is that it would not require changes to the user's object data structures. An example would be: /* * return boolean if doubleclick based on last event info. * value based on time threshold along, which is passed * as a parameter. */ extern short app_check_if_double_click( event, time_threshold ) Event *event; int time_threshold; { static Event last_event; int delta; short ret_value = FALSE; /* only deal with the down events */ if( event_is_up(event) ) return ret_value; if( event_action(event) == ACTION_SELECT && event_action(&last_event) == ACTION_SELECT ) { delta = (event->ie_time.tv_sec - last_event.ie_time.tv_sec) * 1000; delta += event->ie_time.tv_usec / 1000; delta -= last_event.ie_time.tv_usec / 1000; if( delta <= time_threshold ) ret_value = TRUE; } last_event = *event; return ret_value; } Note that in both these cases one must compare against the timeout threshold. The suggested value is in the resource database as OpenWindows.MultiClickTimeout. In the actual textsw code the value used is Mouse.Multiclick.Space. (don't ask - I don't know..) To further insure that the user is interested in a multi-click operation, rather than a press-drag-release operation, the code can include checking for X/Y proximity of the two events. The proper value to be used from the resource database is OpenWindows.DragThreshold. The preferred version for the `definitive doubleclick' takes into account these other factors. Here is a code snippett that also self initializes. /* * return boolean if doubleclick based on last event info. * both time and mouse distance are taken into account. * values are self initializing on first invocation. */ extern short app_check_if_double_click( event ) Event *event; { static Event last_event; static int time_threshold; static int dist_threshold; static short first_time = TRUE; short ret_value = FALSE; int delta_time; int delta_x, delta_y; /* first time this is called init the thresholds */ if( first_time ) { time_threshold = textsw_get_from_defaults(TEXTSW_MULTI_CLICK_TIMEOUT); dist_threshold = textsw_get_from_defaults(TEXTSW_MULTI_CLICK_SPACE); first_time = FALSE; } /* only deal with the down events */ if( event_is_up(event) ) return ret_value; if( event_action(event) == ACTION_SELECT && event_action(&last_event) == ACTION_SELECT ) { delta_time = (event->ie_time.tv_sec - last_event.ie_time.tv_sec) * 1000; delta_time += event->ie_time.tv_usec / 1000; delta_time -= last_event.ie_time.tv_usec / 1000; /* is the time within bounds? */ if( delta_time <= time_threshold ) { /* check to see if the distance is ok */ delta_x = (last_event.ie_locx > event->ie_locx ? last_event.ie_locx - event->ie_locx : event->ie_locx - last_event.ie_locx); delta_y = (last_event.ie_locy > event->ie_locy ? last_event.ie_locy - event->ie_locy : event->ie_locy - last_event.ie_locy); if( delta_x <= dist_threshold && delta_y <= dist_threshold ) ret_value = TRUE; } } last_event = *event; return ret_value; } Further discussion and comment on this topic is encouraged. Hope this helps everyone out! -Jim -- -- Jim Becker / jcb%frisbee@sun.com / Sun Microsystems