;+ ; NAME: ; LIST_SELECTOR ; ; PURPOSE: ; ; The purpose of this function is to implement a pop-up dialog widget ; for the purpose of selecting "names". Names can be names of variables, ; names of files, etc. Any string array can be used. ; ; CALLING SEQUENCE: ; ; selectedNames = List_Selector(theNames) ; ; ARGUMENTS: ; ; theNames: A string array of potential "names" that can be selected. ; ; KEYWORDS: ; ; ALL: Set this keyword if you wish all the names to be selected ; initially. ; ; CANCEL: An output keyword set to 1 if the user cancels or quits the ; program without hitting the Accept button. Set to 0 if a proper ; selection was made and the use hits the Accept button. ; ; COUNT: An output keyword containing the number of elements in the return array. ; ; GROUP_LEADER: The widget identifier of a widget who will be the group leader ; for this dialog. Passing a group leader is the *only* way to ; assure the dialog will be a MODAL dialog (as opposed to a blocking ; dialog). A GROUP_LEADER is required if you will be using this ; function in an IDL Virtual Machine application. ; ; LABEL: A string that will be placed on a label above the selections. ; If not used, no label is used in the program. ; ; LIST_COUNTER: If this keyword is set, a number is associated and displayed with ; each list item, starting with the number 1. ; ; TITLE: A string that is used for the title of the dialog window. If ; undefined, then "Selection Widget" is used. ; ; SELECTED_INDICES: An output vector of the selected indices from theNames array. ; ; RETURN VALUE: ; ; selectedNames: Typically, an array of selected names. If there is only one item ; in the selection, the variable will be a scalar string. ; ; EXAMPLE: ; ; See the List_Selector_Test procedure below. I use the program to allow the ; user to select the names of scientific data sets in an HDF file for further ; reading and processing. ; ; MODIFICATION HISTORY: ; ; Written by David W. Fanning, 11 January 2009, based on Name_Selector program. ; Added "Accept on Double-Click" functionality. 14 January 2009. DWF. ; Added LIST_COUNTER keyword. 25 May 2009. DWF. ; Well, basically a RE-DO of yesterday's work, although done correctly today. 26 May 2009. DWF. ; Fixed a problem when the user double-clicks an item in the list. 8 August 2009. DWF. ; Double clicks are a problem with UNIX machines because sets event.clicks = 2 ; prematurely. Removed double-click functionality from all but Windows machines. 9 Feb 2012. DWF. ; ;- ;******************************************************************************************; ; Copyright (c) 2009-2012, by Fanning Software Consulting, Inc. ; ; All rights reserved. ; ; ; ; Redistribution and use in source and binary forms, with or without ; ; modification, are permitted provided that the following conditions are met: ; ; ; ; * Redistributions of source code must retain the above copyright ; ; notice, this list of conditions and the following disclaimer. ; ; * Redistributions in binary form must reproduce the above copyright ; ; notice, this list of conditions and the following disclaimer in the ; ; documentation and/or other materials provided with the distribution. ; ; * Neither the name of Fanning Software Consulting, Inc. nor the names of its ; ; contributors may be used to endorse or promote products derived from this ; ; software without specific prior written permission. ; ; ; ; THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY ; ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ; ; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ; ; SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, ; ; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ; ; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ; ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ; ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ; ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; ;******************************************************************************************; PRO List_Selector_Button_Events, event Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN ENDIF Widget_Control, event.id, GET_UVALUE=buttonValue Widget_Control, event.top, GET_UVALUE=info CASE buttonValue OF 'SELECT_ALL': Widget_Control, (*info).listID, SET_LIST_SELECT=Indgen(N_Elements((*info).theNames)) 'DESELECT_ALL': Widget_Control, (*info).listID, SET_LIST_SELECT=N_Elements((*info).theNames) 'DISMISS': Widget_Control, event.top, /Destroy 'ACCEPT': BEGIN indices = Widget_Info((*info).listID, LIST_SELECT=1) IF indices[0] EQ -1 THEN count = 0 ELSE count = N_Elements(indices) IF count GT 0 THEN BEGIN *(*info).selectedNamePtr = {cancel:0, theNames:((*info).theNames)[indices], $ selectedIndices:indices} ENDIF ELSE BEGIN void = Dialog_Message('No items were selected. Returning...') RETURN ENDELSE Widget_Control, event.top, /Destroy END ELSE: ENDCASE END ;-------------------------------------------------------------------------------- PRO List_Selector_Click_Events, event ; Events coming here do nothing whatsoever unless event.clicks EQ 2. ; UNIX machines set event.clicks = 2 whenever the Carriage Return is selected, ; which precludes me from using this functionality in anything other than ; Windows machines. IF (event.clicks NE 2) || (StrUpCase(!Version.OS_Family) NE 'WINDOWS') THEN RETURN ; Act as if you had selected this item. Widget_Control, event.top, GET_UVALUE=info indices = event.index IF indices[0] EQ -1 THEN count = 0 ELSE count = N_Elements(indices) IF count GT 0 THEN BEGIN *(*info).selectedNamePtr = {cancel:0, theNames:((*info).theNames)[indices], $ selectedIndices:indices} ENDIF Widget_Control, event.top, /Destroy END ;-------------------------------------------------------------------------------- PRO List_Selector_Size_Events, event ; This event handler handles the size events from the TLB. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN ENDIF Widget_Control, event.top, GET_UVALUE=info Widget_Control, (*info).listID, SCR_XSIZE=event.x > (*info).xmin, $ SCR_YSIZE=(event.y - (*info).yoffset) END ;-------------------------------------------------------------------------------- PRO List_Selector_Test, MORENAMES=morenames, LIST_COUNTER=list_counter, SELECTED_INDICES=selected_indices names = ['dog', 'cow', 'coyote', 'pig', 'elephant', 'donkey', 'aligator', 'croc', $ 'goat', 'snake', 'possum', 'bird', 'eagle', 'hamster'] IF Keyword_Set(morenames) THEN names = [names, names, names] names = List_Selector(names, LABEL='Select Animals for Processing', CANCEL=cancelled, $ LIST_COUNTER=Keyword_Set(list_counter), SELECTED_INDICES=selected_indices) IF ~cancelled THEN BEGIN Print, 'Names: ', names Print, 'Selected Indices: ', selected_indices ENDIF END ;-------------------------------------------------------------------------------- FUNCTION List_Selector, theNames, $ ALL=all, $ CANCEL=cancel, $ COUNT=count, $ LABEL=label, $ LIST_COUNTER=list_counter, $ NUMCOLS=numcols, $ SELECTED_INDICES=selected_indices, $ TITLE=title, $ GROUP_LEADER=group_leader Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN, "" ENDIF ; Check arguments and keywords. IF N_Elements(theNames) EQ 0 THEN Message, 'Must pass string arguments to select.' numNames = N_Elements(theNames) IF Keyword_Set(list_counter) THEN BEGIN CASE 1 OF numNames LT 10: numbers = String(IndGen(numNames)+1, Format='(I1)') numNames LT 100: numbers = String(IndGen(numNames)+1, Format='(I2.2)') numNames LT 1000: numbers = String(IndGen(numNames)+1, Format='(I3.3)') ELSE: numbers = String(IndGen(numNames)+1, Format='(I4.4)') ENDCASE _theNames = numbers + ' ' + theNames ENDIF ELSE _theNames = theNames all = Keyword_Set(all) SetDefaultValue, title, 'Selection Widget' ; Define TLB as a modal widget, if the group leader is present. Otherwise, ; hope like hell it blocks on the XMANAGER command line! :-( IF N_Elements(group_leader) NE 0 THEN BEGIN tlb = Widget_Base(Title=title, COLUMN=1, GROUP_LEADER=group_leader, MODAL=1, $ /BASE_ALIGN_CENTER, /TLB_SIZE_EVENTS) ENDIF ELSE BEGIN tlb = Widget_Base(Title=title, COLUMN=1, /BASE_ALIGN_CENTER, /TLB_SIZE_EVENTS) ENDELSE IF N_Elements(label) NE 0 THEN labelID = Widget_Label(tlb, Value=label) ; Appearance fudges for different machines. IF !D.NAME EQ 'WIN' THEN sizeFudge = 14 ELSE sizeFudge = 18 ; Calculate the size of the selection widget based on how many names you have. ysize = 30 < N_Elements(theNames) listID = Widget_List(tlb, VALUE=_theNames, YSIZE=ysize, /MULTIPLE, EVENT_PRO='List_Selector_Click_Events') buttonBase = Widget_Base(tlb, ROW=1, XPAD=0, YPAD=0, EVENT_PRO='List_Selector_Button_Events') button = Widget_Button(buttonBase, Value='Dismiss', UVALUE='DISMISS') button = Widget_Button(buttonBase, Value='Deselect All', UVALUE='DESELECT_ALL') button = Widget_Button(buttonBase, Value='Select All', UVALUE='SELECT_ALL') button = Widget_Button(buttonBase, Value='Accept', UVALUE='ACCEPT') bgeo = Widget_Info(buttonBase, /GEOMETRY) ; Set all the buttons to selected? IF all THEN BEGIN FOR j=0,numNames-1 DO Widget_Control, listID, SET_LIST_SELECT=Indgen(N_Elements(theStrings)) ENDIF ; Display pop-up widgets in the center of the display. cgCenterTLB, tlb ; Need sizes for sizing the TLB, if required. IF N_Elements(labelID) NE 0 THEN BEGIN labelGeo = Widget_Info(labelID, /GEOMETRY) label_y = labelGeo.scr_ysize ENDIF ELSE label_y = 0.0 butGeo = Widget_Info(buttonBase, /GEOMETRY) yoffset = label_y + butGeo.scr_ysize xmin = butGeo.scr_xsize ; Minimum size for selection base. sbgeo = Widget_Info(listID, /GEOMETRY) IF sbgeo.scr_xsize LT xmin THEN BEGIN Widget_Control, listID, SCR_XSIZE=xmin ENDIF Widget_Control, tlb, /Realize ; Set up pointers for passing info around the program and for storing results. selectedNamePtr = Ptr_New({cancel:1}) info = Ptr_New({listID:listID, selectedNamePtr:selectedNamePtr, $ theNames:theNames, yoffset:yoffset, xmin:xmin}, /NO_COPY) Widget_Control, tlb, SET_UVALUE=info ; This *should* block the IDL command line, but if you called this from ; a blocking widget program, you could run though this block with disasterous ; results. This is a *common* problem with Virtual Machine applications, which ; by definition block the IDL command line. Maybe you should have used a GROUP_LEADER. XManager, 'name_selector', tlb, EVENT_HANDLER='List_Selector_Size_Events' ; Return here after block is released. Set the CANCEL flag, get the names, free the pointers. cancel = (*selectedNamePtr).cancel IF cancel EQ 0 THEN BEGIN selectedNames = (*selectedNamePtr).theNames selected_indices = (*selectedNamePtr).selectedIndices ENDIF ELSE selectedNames = "" IF N_Elements(selected_indices) EQ 1 THEN selected_indices = selected_indices[0] Ptr_Free, selectedNamePtr, info ; Count them. count = N_Elements(selectedNames) ; Return the selected names. IF N_Elements(selectedNames) EQ 1 THEN RETURN, selectedNames[0] ELSE RETURN, selectedNames END ; ----------------------------------------------------------------------------------