; $Id: online_help_pdf_index.pro,v 1.26 2004/06/11 16:48:04 ddirks Exp $ ; ; Copyright (c) 2004, Research Systems, Inc. All rights reserved. ; Unauthorized reproduction prohibited. ;+ ; NAME: ; ONLINE_HELP_PDF_INDEX ; ; PURPOSE: ; Widget utility that allows the user to select an entry from ; the IDL documentation master index, then display the selected ; page in the Adobe Acrobat viewer. This is meant to be called ; by ONLINE_HELP on UNIX systems when the supplied topic name ; does not exist. ; ; This utility only works on platforms that support the IDL-Acrobat ; plug-in: currently all Unix platforms except AIX and OS X. ; ; This utility relies on the presence of a file containing a ; text-only copy of the IDL Master Index document. The file is ; located in the help subdirectory of the IDL distribution, and ; is called 'mindex.txt'. ; ; CATEGORY: ; Help, documentation. ; ; CALLING SEQUENCE: ; ONLINE_HELP_PDF_INDEX [, TopicString] [, /EXACT] ; ; INPUTS: ; TopicString: A scalar string containing the item about which ; help is desired. ; ; KEYWORD PARAMETERS: ; EXACT: Set this keyword to specify that TopicString is ; an exact match of a valid named destination (as returned ; by the ONLINE_HELP_PDF_ND2FILE function). ; ; Note: this keyword is not documented and its behavior ; may change in future releases. It is inteded for use by ; the ONLINE_HELP procedure and ? command, which know how ; to determine whether a string is in fact a valid desintation. ; ; OUTPUTS: ; Display the widget interface. If the EXACT keyword is set, ; (implying that TopicString exactly matches a naed destination ; in one of the IDL docset PDF files), check the saved ; preferences to determine whether the widget interface should ; be displayed. ; ; COMMON BLOCKS: ; ONLINE_HELP_PDF_INDEX - This block is private to this module, ; and not to be used by other routines. ; ; MODIFICATION HISTORY: ; 29 April 2004, DD ; ;- ; ;--------------------------------------------------------------------- FUNCTION PickBook, book ;{{{ ; Function to match the book abbreviation string used in the ; master index file to the 'real' name of the PDF file. COMPILE_OPT idl2, hidden CASE book of 'Bld': BookName='building.pdf' 'DM': BookName='datamine.pdf' 'EDG': BookName='edg.pdf' 'GS': BookName='getstart.pdf' 'Img': BookName='image.pdf' 'Inst': BookName='instlic.pdf' 'IonI': BookName='ionintro.pdf' 'IonJ': BookName='ionjava.pdf' 'IonQ': BookName='ionquickref.pdf' 'IonS': BookName='ionscrpt.pdf' 'ITD': BookName='itooldevguide.pdf' 'ITU': BookName='itooluserguide.pdf' 'Med': BookName='medical.pdf' 'MIDX': BookName='mindex.pdf' 'Obs': BookName='obsolete.pdf' 'Onlg': BookName='onlguide.pdf' 'Quik': BookName='quickref.pdf' 'Ref': BookName='refguide.pdf' 'SDF': BookName='sdf.pdf' 'Use': BookName='using.pdf' 'Wav': BookName='wavelet.pdf' 'WN': BookName='whatsnew.pdf' else: BookName='unknown book' ENDCASE RETURN, BookName END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO ShowHelp, state ;{{{ ; Procedure to display the selected page. COMPILE_OPT idl2, HIDDEN bookpath = !HELP_PATH + PATH_SEP() + (*state).book bookinfo = FILE_INFO(bookpath) IF bookinfo.exists THEN BEGIN IF (*state).page && (*state).book THEN BEGIN IF !VERSION.OS_FAMILY EQ 'Windows' THEN BEGIN PRINT, 'Display page ', (*state).page, ' of ', (*state).book ENDIF ELSE BEGIN ONLINE_HELP, BOOK=(*state).book, PAGE=(*state).page (*state).clicks=0 ENDELSE ENDIF ELSE BEGIN PRINT, 'Nothing selected.' ENDELSE ENDIF ELSE BEGIN err_message = ['The file '+(*state).book+' was not found', $ 'in the help directory of the IDL distribution.'] void = DIALOG_MESSAGE(err_message, DIALOG_PARENT=(*state).tbase, $ /ERROR, TITLE='PDF File Not Found') ENDELSE END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- FUNCTION GetBookmarks, PREFS=prefs, BOOKFILE=bkfile; {{{ ; Function to read bookmarks from a file in the user's ; .idl directory. COMPILE_OPT idl2, HIDDEN prefs = { show_always:1, xpos:0.0, ypos:0.0, width:350.0, height:650.0 } ; Text of the application readme file. app_desc = [ $ 'This is the configuration directory for the IDL online', $ 'help system. It is used to store settings between IDL sessions.', $ '', $ 'It is safe to remove this directory, as it will be recreated', $ 'on demand. Note that any bookmarks you have created will be', $ 'deleted.'] ; This is version 1 of the application readme file. app_version=1 ; Get the path to the application user directory. app_dir_path = APP_USER_DIR('RSI', 'Research Systems, Inc.', $ 'help', 'IDL Online Help System', app_desc, $ app_version, /RESTRICT_IDL_RELEASE) bkfile = app_dir_path+PATH_SEP()+'pdf_bookmarks.txt' ; Get info on bookmarks file. fileInfo = FILE_INFO(bkfile) ; If file exists and is readable, read into bkmarks array. IF fileInfo.read THEN BEGIN ; Create an array to hold the bookmarks nlines=LONG(FILE_LINES(bkfile)) bkmarks=strarr(nlines) ; Open the file OPENR, unit, bkfile, /GET_LUN, ERROR=err ; Exit if error occurs IF (err NE 0) THEN BEGIN void = DIALOG_MESSAGE(!ERROR_STATE.MSG) RETURN, ['There was an error reading the bookmarks file.'] ENDIF ; Read the contents of the file into the 'bkmarks' array. line='' FOR i = 0l, nlines-1 DO BEGIN READF, unit, line IF ~(STRMATCH(line, ';*')) THEN bkmarks[i] = line IF (STRMATCH(line, '; PREF_Show_Always*')) THEN BEGIN prefs.show_always = STRMID(STREGEX(line, '=.*', /EXTRACT), 1) ENDIF IF (STRMATCH(line, '; PREF_xpos*')) THEN BEGIN prefs.xpos = STRMID(STREGEX(line, '=.*', /EXTRACT), 1) ENDIF IF (STRMATCH(line, '; PREF_ypos*')) THEN BEGIN prefs.ypos = STRMID(STREGEX(line, '=.*', /EXTRACT), 1) ENDIF IF (STRMATCH(line, '; PREF_width*')) THEN BEGIN prefs.width = STRMID(STREGEX(line, '=.*', /EXTRACT), 1) ENDIF ;IF (STRMATCH(line, '; PREF_height*')) THEN BEGIN ; prefs.height = STRMID(STREGEX(line, '=.*', /EXTRACT), 1) ;ENDIF ENDFOR ; Remove blank lines. bkmarks = bkmarks[WHERE(bkmarks NE '')] ; Close the file FREE_LUN, unit ; If the array is empty (meaning the file was, too), ; create a default bookmark. IF (bkmarks[0] EQ '' && N_ELEMENTS(bkmarks) EQ 1) THEN $ bkmarks = ['Default Bookmark: Online Guide, 1 Onlg'] ENDIF ELSE BEGIN ; If no bookmarks file exists, just start with a default ; bookmark. bkmarks = ['Default Bookmark: Online Guide, 1 Onlg'] ENDELSE RETURN, bkmarks END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO AddBookmark, ev ; {{{ ; Procedure to add a bookmark. COMPILE_OPT idl2, HIDDEN ; Get state structure WIDGET_CONTROL, ev.TOP, GET_UVALUE=state ; Get contents of the bookmark manager widgets WIDGET_CONTROL, (*state).bkText, GET_VALUE=bkText bkBook = WIDGET_INFO((*state).bkBooks, /DROPLIST_SELECT) WIDGET_CONTROL, (*state).bkPnum, GET_VALUE=bkPnum ; Check values of bookmark manager widgets IF (bkText EQ '') THEN BEGIN void = DIALOG_MESSAGE('Please supply bookmark text.') RETURN ENDIF IF (bkPnum EQ '') THEN BEGIN void = DIALOG_MESSAGE('Please supply a page number.') RETURN ENDIF IF (~(STREGEX(bkPnum, '^[0-9]*$', /BOOLEAN))) THEN BEGIN void = DIALOG_MESSAGE('Page numbers must be integers.') RETURN ENDIF ; Build the new bookmark newBkMark = bkText+', '+bkPnum+' '+((*state).book_abr)[bkBook] ; Update the bookmark list bkmarks = [*(*state).bkmarks, [newBkMark]] *(*state).bkmarks = bkmarks ; Write out the changed bookmarks WriteBookmarks, ev ; Populate the text widget with the new bkmarks WIDGET_CONTROL, (*state).bklist, SET_VALUE=bkmarks ; Send an event to update the selection highlight list_select_ev, { WIDGET_TEXT_SEL, ID:(*state).bklist, TOP:(*state).tbase, $ HANDLER:(*state).bklist, TYPE:3, OFFSET:FIX(TOTAL(STRLEN(bkmarks))), $ LENGTH:0L } END ; }}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO EditBookmark, ev ; {{{ ; Procedure to edit the selected bookmark. COMPILE_OPT idl2, HIDDEN ; Get state structure WIDGET_CONTROL, ev.TOP, GET_UVALUE=state ; Get contents of the bookmark manager widgets WIDGET_CONTROL, (*state).bkText, GET_VALUE=bkText bkBook = WIDGET_INFO((*state).bkBooks, /DROPLIST_SELECT) WIDGET_CONTROL, (*state).bkPnum, GET_VALUE=bkPnum ; Check values of bookmark manager widgets IF (bkText EQ '') THEN BEGIN void = DIALOG_MESSAGE('Please supply bookmark text.') RETURN ENDIF IF (bkPnum EQ '') THEN BEGIN void = DIALOG_MESSAGE('Please supply a page number.') RETURN ENDIF IF (~(STREGEX(bkPnum, '^[0-9]*$', /BOOLEAN))) THEN BEGIN void = DIALOG_MESSAGE('Page numbers must be integers.') RETURN ENDIF ; Build the edited bookmark newBkMark = bkText+', '+bkPnum+' '+((*state).book_abr)[bkBook] ; Update the bookmark list bkmarks = *(*state).bkmarks bkmarks[(*state).line] = [newBkMark] *(*state).bkmarks = bkmarks ; Write out the changed bookmarks WriteBookmarks, ev ; Populate the text widget with the new bkmarks WIDGET_CONTROL, (*state).bklist, SET_VALUE=bkmarks ; Send an event to update the selection highlight list_select_ev, { WIDGET_TEXT_SEL, ID:(*state).bklist, TOP:(*state).tbase, $ HANDLER:(*state).bklist, TYPE:3, OFFSET:(*state).linebegin, $ LENGTH:0L } END ; }}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO DeleteBookMark, ev ; {{{ ; Procedure to delete a bookmark from the bookmarks list. COMPILE_OPT idl2, HIDDEN ; Get the state WIDGET_CONTROL, ev.TOP, GET_UVALUE=state ; Get the column and row version of the cursor offset sel_colrow = WIDGET_INFO((*state).bklist, TEXT_OFFSET_TO_XY=(*state).selection[0]) ; Update the bookmark list bkmarks = *(*state).bkmarks IF (N_ELEMENTS(bkmarks) GT 1) THEN BEGIN IF (sel_colrow[1] GT 0) THEN BEGIN bkmarks[sel_colrow[1]]='' ENDIF ELSE BEGIN bkmarks[0]='' ENDELSE bkmarks = bkmarks[WHERE(bkmarks NE '')] ENDIF ELSE BEGIN bkmarks = ['Default Bookmark: Online Guide, 1 Onlg'] ENDELSE *(*state).bkmarks = bkmarks ; Write out the changed bookmarks WriteBookmarks, ev ; Populate the text widget with the new bkmarks WIDGET_CONTROL, (*state).bklist, SET_VALUE=bkmarks ; Send an event to update the selection highlight list_select_ev, { WIDGET_TEXT_SEL, ID:(*state).bklist, TOP:(*state).tbase, $ HANDLER:(*state).bklist, TYPE:3, OFFSET:(*state).linebegin, $ LENGTH:0L } END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO WriteBookmarks, ev ;{{{ ; Procedure to write out the bookmarks file. COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state bkmarks = *(*state).bkmarks ; Open the bookmarks file OPENW, unit, (*state).bkfile, /GET_LUN, ERROR=err ; Exit if error occurs IF (err NE 0) THEN BEGIN void = DIALOG_MESSAGE(!ERROR_STATE.MSG) RETURN ENDIF ; Write comment block to bookmarks file PRINTF, unit, '; ------------------------------------------------------' PRINTF, unit, '; ONLINE_HELP_PDF_INDEX Bookmarks file' PRINTF, unit, '; ' PRINTF, unit, '; This file contains bookmarks for the PDF help' PRINTF, unit, '; system index utility. You can make changes via' PRINTF, unit, '; the index utility, or by editing this file directly.' PRINTF, unit, '; See ONLINE_HELP_PDF_INDEX in the IDL Reference Guide' PRINTF, unit, '; for details.' PRINTF, unit, '; ' PRINTF, unit, '; Note: do not make changes to this comment block; they' PRINTF, unit, '; will NOT be preserved.' PRINTF, unit, '; ' PRINTF, unit, '; PREF_Show_Always=', $ STRCOMPRESS((*state).show_always, /REMOVE_ALL) PRINTF, unit, '; PREF_xpos=', STRCOMPRESS((*state).xpos, /REMOVE_ALL) PRINTF, unit, '; PREF_ypos=', STRCOMPRESS((*state).ypos, /REMOVE_ALL) PRINTF, unit, '; PREF_width=', STRCOMPRESS((*state).width, /REMOVE_ALL) PRINTF, unit, '; PREF_height=', STRCOMPRESS((*state).height, /REMOVE_ALL) PRINTF, unit, '; ------------------------------------------------------' ; Write the bookmarks FOR i = 0l, N_ELEMENTS(bkmarks)-1 DO BEGIN PRINTF, unit, bkmarks[i] ENDFOR ; Close the file FREE_LUN, unit END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO HighlightPageNum, ev ;{{{ ; Procedure to highlight the page number and book name defined ; by state.selection. COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state selection = (*state).selection IF (WIDGET_INFO((*state).wTab, /TAB_CURRENT) EQ 0) THEN BEGIN currlist = (*state).ixlist ENDIF ELSE BEGIN currlist = (*state).bklist ENDELSE ; Highlight the selected page number/book name WIDGET_CONTROL, currlist, SET_TEXT_SELECT=[selection[0], selection[1]] ; Get the value of the selection WIDGET_CONTROL, currlist, GET_VALUE=string, /USE_TEXT_SELECT ; Parse the string for page number and book name page = LONG((STREGEX(string, '[0-9]+', /EXTRACT))[0]) book = (STREGEX(string, '[A-Za-z]+', /EXTRACT))[0] ; Find the name of the PDF file that corresponds to book pdf = PickBook(book) ; Store the info (*state).page = page (*state).book = pdf IF (WIDGET_INFO((*state).wTab, /TAB_CURRENT) EQ 1) THEN BEGIN curr_line = (*state).line < (N_ELEMENTS(*(*state).bkmarks)-1) > 0 bkmark = (STRSPLIT((*(*state).bkmarks)[curr_line], ',', /EXTRACT))[0] bkidx = WHERE((*state).book_abr EQ book) bkpage = (STREGEX(string, '[0-9]+', /EXTRACT))[0] WIDGET_CONTROL, (*state).bkText, SET_VALUE=bkmark WIDGET_CONTROL, (*state).bkBooks, SET_DROPLIST_SELECT=bkidx WIDGET_CONTROL, (*state).bkPnum, SET_VALUE=bkpage ENDIF END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- FUNCTION SelectPageNum, ev, sel_offset_line, sel_colrow, list ;{{{ ; Function to define a range that corresponds to a page number/ ; book name pair. If The user clicks on a number/name pair, that ; pair is selected. If the user clicks in front of the first pair, ; the first pair is selected. If the user clicks on a line that ; has no number/name pair, the next available pair is selected. ; ; Takes the offset to the beginning of the selected line, the ; column/row offset, and the list being displayed in the widget. COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state ; Init two arrays selection = [0l, 0l] sel = [0l, 0l] nlines = N_ELEMENTS(list) ; Retrieve the text of the selected line. IF ((sel_colrow[1] GE 0) && (sel_colrow[1] LT nlines)) THEN BEGIN line = (list)[sel_colrow[1]] ENDIF ELSE BEGIN ; If the user clicks past the end of the list, select the ; last line. line = (list)[nlines-1] sel_colrow = [0,0] sel_offset_line = WIDGET_INFO(ev.id, TEXT_XY_TO_OFFSET=[0,nlines-1]) ENDELSE ; Split the selected line on ', ' followed by a number. This ; keeps multipart index text entries (like "doing, this, 123 Ref") ; from showing up with the text portion broken into multiple ; substrings. substrings = STRSPLIT(line, ', [0-9]', /REGEX) ; Subtract 1 from the substring indices to account for the ; integer in the match substrings = substrings-1 ; The first element should always be zero. substrings[0] = 0 ; Add the end of the selected line to the array. substrings = [substrings,STRLEN(line)] ; Number of elements of the array nstrings = N_ELEMENTS(substrings) ; If the line contains a number/name pair... IF ((nstrings gt 2) && (STREGEX(line, '[0-9]+', /BOOLEAN))) THEN BEGIN ; Locate the selection within the current line ; and set the sel array to the beginning and ending ; selection values. FOR ns = 0, nstrings-1 DO BEGIN IF (sel_colrow[0] gt substrings[ns]) THEN BEGIN sel[0] = substrings[ns] sel[1] = substrings[ns+1] ENDIF ENDFOR ; If the selection is in the first substring, select ; the second substring. IF (sel_colrow[0] le substrings[1]) THEN BEGIN sel[0] = substrings[1] sel[1] = substrings[2] ENDIF ; Reset the clicks field (*state).clicks=0 ; If the line doesn't have a number/name pair... ENDIF ELSE BEGIN ; If we've moved up... IF (sel_colrow[1] lt (*state).line) THEN BEGIN ; Decrease the row number by 1 IF (sel_colrow[1] LT nlines) THEN sel_colrow[1]=sel_colrow[1]-1 ; Move sel_offset_line to the beginning of the previous line sel_offset_line = WIDGET_INFO(ev.id, $ TEXT_XY_TO_OFFSET=[0,sel_colrow[1]]) ; Set the column number to 0 sel_colrow[0]=0 ; Call this function with the new values selection = SelectPageNum(ev, sel_offset_line, sel_colrow, list) ; Reset the clicks field (*state).clicks=0 ; Return the result. RETURN, selection ; If we've moved down... ENDIF ELSE BEGIN ; Move sel_offset_line to the beginning of the next line sel_offset_line = sel_offset_line+STRLEN(line)+1 ; Increase the row number by 1 IF (sel_colrow[1] LT nlines) THEN sel_colrow[1]=sel_colrow[1]+1 ; Set the column number to 0 sel_colrow[0]=0 ; Call this function with the new values selection = SelectPageNum(ev, sel_offset_line, sel_colrow, list) ; Reset the clicks field (*state).clicks=0 ; Return the result. RETURN, selection ENDELSE ENDELSE ; Build the returned selection array. Note that it contains ; the offset to the beginning of the selection, and the length ; of the selection. selection[0]=sel[0]+sel_offset_line selection[1]=sel[1]-sel[0] ; Return the result. RETURN, selection END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- ; Event handlers for UI controls {{{ PRO Help_ev, ev ; Help button COMPILE_OPT idl2, HIDDEN ; Just open the correct topic in the IDL Reference Guide. ONLINE_HELP, 'online_help_pdf_index' END PRO Done_ev, ev ; Done button COMPILE_OPT idl2, HIDDEN WriteBookmarks, ev WIDGET_CONTROL, ev.TOP, /DESTROY END PRO Go_ev, ev ; Display button COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state ShowHelp, state END PRO tab_change_ev, ev ; Do this when the user switches tabs. COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state ; If we've selected the bookmarks tab, send a list select event. IF (WIDGET_INFO((*state).wTab, /TAB_CURRENT) EQ 1) THEN BEGIN list_select_ev, { WIDGET_TEXT_SEL, ID:ev.id, TOP:ev.top, $ HANDLER:ev.handler, TYPE:3, OFFSET:1, LENGTH:0L } ENDIF END PRO showpref_ev, ev ; Do this when the user clicks or unclicks the "Show Always" ; checkbox. COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state (*state).show_always=WIDGET_INFO(ev.ID, /BUTTON_SET) END PRO online_help_pdf_index_event, ev COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state tbase_g = WIDGET_INFO(ev.TOP, /GEOMETRY) CASE TAG_NAMES(ev, /STRUCTURE_NAME) OF 'WIDGET_KILL_REQUEST': Done_ev, ev 'WIDGET_BASE': BEGIN ; Resize ; Calculate change in width IF (ev.x EQ 0) THEN BEGIN deltaX = 0 ENDIF ELSE BEGIN deltaX = ev.x - (*state).width ENDELSE newX = (*state).width + deltaX ; NOTE: Height cannot currently be changed. ; Calculate change in height ;IF (ev.y EQ 0) THEN BEGIN ; deltaY = 0 ;ENDIF ELSE BEGIN ; deltaY = ev.y - (*state).height ;ENDELSE ;newY = (*state).height + deltaY ; Adjust width of child widgets IF ((deltaX NE 0) || (ev.x EQ 0)) THEN BEGIN WIDGET_CONTROL, ev.TOP, XSIZE=(((*state).width=newX-(tbase_g.xpad*2))) WIDGET_CONTROL, (*state).ixlist, SCR_XSIZE=newX-(tbase_g.xpad*6) WIDGET_CONTROL, (*state).bklist, SCR_XSIZE=newX-(tbase_g.xpad*8) WIDGET_CONTROL, (*state).bkbase3, SCR_XSIZE=newX-(tbase_g.xpad*10) WIDGET_CONTROL, (*state).bkbase4, SCR_XSIZE=newX-(tbase_g.xpad*8) labelwidth = (WIDGET_INFO((*state).bkLabel1,/GEOMETRY)).xsize WIDGET_CONTROL, (*state).bkText, $ SCR_XSIZE=newX-(tbase_g.xpad*16)-labelwidth ENDIF WIDGET_CONTROL, ev.TOP, YSIZE=(*state).height ; NOTE: Height cannot currently be changed. ; Adjust height of child widgets ;IF ((deltaY NE 0) || (ev.y EQ 0)) THEN BEGIN ; WIDGET_CONTROL, ev.TOP, YSIZE=(((*state).height=newY-(tbase_g.ypad*2))) ; bklheight = (WIDGET_INFO((*state).bklist, /GEOMETRY)).scr_ysize ; ixlheight = (WIDGET_INFO((*state).ixlist, /GEOMETRY)).scr_ysize ; WIDGET_CONTROL, (*state).bklist, SCR_YSIZE=((bklheight+deltaY)>80) ; newbklheight = (WIDGET_INFO((*state).bklist, /GEOMETRY)).scr_ysize ; deltabkl = newbklheight - (bklheight+deltaY) ; WIDGET_CONTROL, ev.TOP, YSIZE=(((*state).height=newY+deltabkl)) ; WIDGET_CONTROL, (*state).ixlist, SCR_YSIZE=(ixlheight+deltaY+deltabkl) ;ENDIF ; Get the current size of the top level base and update ; the state structure. tbase_g = WIDGET_INFO(ev.TOP, /GEOMETRY) (*state).width = tbase_g.xsize END 'WIDGET_TLB_MOVE': BEGIN ; Move (*state).xpos = tbase_g.xoffset (*state).ypos = tbase_g.yoffset END 'WIDGET_KBRD_FOCUS': BEGIN ; Track whether the index has focus. We use this information ; to disable updates in the index list when selections are ; created in other applciations, causing the list to get a ; deselection event. IF (ev.ID EQ (*state).tbase) THEN BEGIN (*state).hasFocus = ev.ENTER ENDIF END 'WIDGET_TLB_ICONIFY': BEGIN ; Track whether the index has been iconified, and set the ; focus flag appropriately. IF (ev.ID EQ (*state).tbase) THEN BEGIN (*state).hasFocus = ev.ICONIFIED ? 0 : 1 ENDIF END ELSE : ENDCASE END PRO nothing_ev, ev ; Swallow events generated by widgets in the 'Manage Bookmarks' ; section of the bookmarks tab. ; This event handler would normally do nothing, but we have ; to work around the behavior of text widgets in Motif, wherein ; creating a selection in one text widget removes the selection ; from another widget. In this case, this means that there is ; a selection event generated in the bookmark list, which in ; turn triggers the selection highlighting and possibly the ; ShowHelp function. So we set a "context change" flag here, ; and test for it in list_select_event, resetting the flag ; there if it is found. WIDGET_CONTROL, ev.TOP, GET_UVALUE=state (*state).ccFlag=1 END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO list_select_ev, ev ;{{{ ; Event handler for mouse/cursor movement in the index list. COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state ; Only do the list select if the index GUI has keyboard focus. IF ((*state).hasFocus EQ 0) THEN RETURN ; Check whether we're in the index list or the bookmarks list IF (WIDGET_INFO((*state).wTab, /TAB_CURRENT) EQ 0) THEN BEGIN currlist = *(*state).list ENDIF ELSE BEGIN currlist = *(*state).bkmarks ENDELSE lines = N_ELEMENTS(currlist) ; If we've got a text select event... {{{ IF (TAG_NAMES(ev, /STRUCTURE_NAME) EQ 'WIDGET_TEXT_SEL') THEN BEGIN ; We have to check to see if the event was caused by the ; removal of the selection in the bookmark list due to the ; creation of a selection elsewhere. See the nothing_ev ; procedure for more details. IF ((ev.LENGTH EQ 0) && ((*state).ccFlag)) THEN BEGIN (*state).ccFlag = 0 RETURN ENDIF ; ------------------------------------------------------------- ; This whole next block of code tries to do the right ; thing with arrow keys, which are not well supported ; under Motif. ; ; Get the selection beg_sel = (*state).selection[0] end_sel = (*state).selection[0]+(*state).selection[1] ; Get the column and row version of the cursor offset sel_colrow = WIDGET_INFO(ev.id, TEXT_OFFSET_TO_XY=ev.offset) ; Get the integer offset to the beginning of the selected line sel_offset_line = WIDGET_INFO(ev.id, TEXT_XY_TO_OFFSET=[0,sel_colrow[1]]) ; Figure out where we are, and where the commas are sel_begin = beg_sel - sel_offset_line curr_line = sel_colrow[1] < (lines-1) > 0 commas=STRSPLIT(currlist[curr_line], ',') ; Here we try to be smart about where to move when the ; user presses the left arrow key. IF (ev.offset EQ end_sel-1) THEN BEGIN (*state).clicks=0 IF (N_ELEMENTS(commas) GT 1) THEN BEGIN IF ((sel_begin-4) LE commas[1]) THEN BEGIN ; Get the integer offset to the beginning of ; the previous line IF (sel_colrow[1] NE 0) THEN BEGIN new_offset = WIDGET_INFO(ev.id, $ TEXT_XY_TO_OFFSET=[0,(*state).line-1]) ENDIF ELSE BEGIN new_offset = WIDGET_INFO(ev.id, $ TEXT_XY_TO_OFFSET=[0,(*state).line]) ENDELSE ENDIF ELSE BEGIN ; Move the offset back to before the previous ; page number. new_offset = ev.offset-10 ENDELSE ENDIF ELSE BEGIN ; Get the integer offset to the beginning of ; the previous line IF (sel_colrow[1] NE 0) THEN BEGIN new_offset = WIDGET_INFO(ev.id, $ TEXT_XY_TO_OFFSET=[0,(*state).line-1]) ENDIF ELSE BEGIN new_offset = WIDGET_INFO(ev.id, $ TEXT_XY_TO_OFFSET=[0,(*state).line]) ENDELSE ENDELSE (*state).clicks=0 list_select_ev, { WIDGET_TEXT_SEL, ID:ev.id, TOP:ev.top, $ HANDLER:ev.handler, TYPE:3, OFFSET:new_offset, $ LENGTH:0L } ;(*state).clicks=0 RETURN ENDIF ; End of arrow key processing code. ; ------------------------------------------------------------- ; First, if the offset is exactly the same as the saved ; offset, treat it like a double-click. ; Otherwise, check to see if the current selection is within ; the existing selection. If it is, update the clicks field ; of the state strucutre. This allows us to do something that ; looks like "double-click to select" if there can be an infinite ; amount of time between clicks; we need to do this because there ; is no 'clicks' field in the WIDGET_TEXT_SEL event structure. ; Note that this has the side effect of treating two consecutive ; identical selection events as a double-click, no matter how ; they come in. For example, hitting the down arrow at the end ; of either index or bookmarks list multiple times will open ; the PDF file to the selected page. IF (ev.offset EQ (*state).offset) THEN BEGIN HighlightPageNum, ev (*state).clicks++ ENDIF ELSE BEGIN IF ((ev.offset ge (*state).selection[0]) AND $ (ev.offset le (*state).selection[0]+(*state).selection[1])) $ THEN BEGIN ; Display the selected page (*state).clicks++ (*state).offset = ev.offset ; Highlight the page number/book name string. HighlightPageNum, ev ENDIF ELSE BEGIN ; Get the column and row version of the cursor offset sel_colrow = WIDGET_INFO(ev.id, TEXT_OFFSET_TO_XY=ev.offset) ; Get the integer offset to the beginning of the selected line sel_offset_line = WIDGET_INFO(ev.id, $ TEXT_XY_TO_OFFSET=[0,sel_colrow[1]]) ; Calculate the offsets for the page number/book name string selection = SelectPageNum(ev, sel_offset_line, sel_colrow, currlist) ; Update the state with the new selection (*state).selection = selection ; Make sure we don't wrap around the end of the index or ; bookmarks list. IF ((sel_colrow[1] EQ 0) && (lines GT 1) && (ev.offset GT 20)) $ THEN sel_colrow[1] = lines-1 (*state).line = sel_colrow[1] (*state).linebegin = sel_offset_line (*state).clicks = 0 (*state).offset = ev.offset ; Highlight the page number/book name string. HighlightPageNum, ev ENDELSE ENDELSE ENDIF ;}}} ; If we've got a keyboard character event... {{{ IF (TAG_NAMES(ev, /STRUCTURE_NAME) EQ 'WIDGET_TEXT_CH') THEN BEGIN CASE ev.ch OF 9b: BEGIN ; Tab key WIDGET_CONTROL, (*state).ixtext, /INPUT_FOCUS list_select_ev, { WIDGET_TEXT_SEL, ID:ev.id, TOP:ev.top, $ HANDLER:ev.handler, TYPE:3, OFFSET:ev.offset, $ LENGTH:0L } END 10b: ShowHelp, state ; Enter key ELSE: ENDCASE ENDIF ;}}} ; Check the clicks field of the state structure. If the value ; is more than one, display the selected topic and reset the ; field. IF (*state).clicks GT 1 THEN BEGIN ShowHelp, state (*state).clicks=0 ENDIF END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO list_search_ev, ev ;{{{ ; Event handler for the text input field. Typing in the field ; performs an incremental search in the index list, moving the ; selection in the index list to the first match. COMPILE_OPT idl2, HIDDEN WIDGET_CONTROL, ev.TOP, GET_UVALUE=state ; Set the "context change" flag to specify that a select can ; be made in the text input field. This keeps the index list ; from updating its selection when a selection is created ; in the text input field. (*state).ccFlag=1 ; Catch some special keys: IF (TAG_NAMES(ev, /STRUCTURE_NAME) EQ 'WIDGET_TEXT_CH') THEN BEGIN CASE ev.ch OF 9b: WIDGET_CONTROL, (*state).ixlist, /INPUT_FOCUS ; Tab 10b: ShowHelp, state ; Enter ELSE: ENDCASE ENDIF ; Get the value of the string. WIDGET_CONTROL, ev.ID, GET_VALUE=str ; Get the number of lines in the list lines = N_ELEMENTS(*(*state).list) ; Retrieve the value of the element in the 'itable' array that ; corresponds to the byte value of the first character in the ; input string. This element contains the index of the array ; element that contains the first instance of that character in ; the index array. IF (str NE '') THEN $ i=(*state).itable[BYTE(STRLOWCASE(STRMID(str,0,1)))] $ ELSE i=0 ; Retrieve the value of the _next_ element in the 'itable' array ; that is not -1. This value is used to limit the search to ; lines that begin with the first character typed. j = -1 & k = 1 WHILE (j eq -1) DO BEGIN j=(*state).itable[BYTE(STRLOWCASE(STRMID(str,0,1))) + k++] IF (j lt i) THEN j=-1 ENDWHILE ; Initialize the loop control variable go=1 WHILE (go) DO BEGIN ; Compare the string in the text widget to each string in the ; list until a match is found. Note that we start searching with ; the first line in the list that contains the first character ; typed, but after that it's a brute-force linear search. IF STRCMP(STRLOWCASE(str), STRLOWCASE((*(*state).list)[i]), $ STRLEN(str)) THEN BEGIN ; Get the offset to the beginning of the line that matches. sel_offset_line = WIDGET_INFO((*state).ixlist, TEXT_XY_TO_OFFSET=[0,i]) ; ; Scroll the widget to put that line at the top. WIDGET_CONTROL, (*state).ixlist, SET_TEXT_TOP_LINE=i ; Calculate the offsets for the page number/book name string selection = SelectPageNum(ev, sel_offset_line, [0,i], *(*state).list) ; Update the state with the new selection (*state).selection = selection ; Highlight the page number/book name string. HighlightPageNum, ev ; Escape the loop go=0 ENDIF ELSE BEGIN ; Increment the counter IF (i lt j) THEN i++ ELSE go=0 ENDELSE ENDWHILE END ;}}} ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- PRO online_help_pdf_index, topic, EXACT=exact ; This is the main procedure for the PDF documentation index ; widget application. COMPILE_OPT idl2 ; Currently, this routine only runs under UNIX. IF !VERSION.OS_FAMILY EQ 'Windows' THEN BEGIN MESSAGE, 'This routine runs only under UNIX.', /CONTINUE RETURN ENDIF ; We need to store the widget ID of the top-level base in a ; common block to be able to control the app from the IDL ; command line COMMON ONLINE_HELP_PDF_INDEX, tbase ; There should be exactly one argument, and it should have ; no leading or trailing whitespace. IF ( N_ELEMENTS(topic) NE 1 ) THEN topic='' topic = STRCOMPRESS(topic, /REMOVE_ALL) ; The EXACT keyword indicates that topic is a known Named ; Destination that can be displayed directly by ONLINE_HELP. IF ~KEYWORD_SET(exact) THEN exact='0' ; If the app is already running, first clear the text field ; and then populate it with the value of 'topic'. IF ( XREGISTERED('online_help_pdf_index') NE 0 ) THEN BEGIN WIDGET_CONTROL, tbase, GET_UVALUE=state WIDGET_CONTROL, (*state).ixtext, SET_VALUE='' (*state).hasFocus = 1 list_search_ev, {WIDGET_TEXT_CH, ID:(*state).ixtext, TOP:(*state).tbase, $ HANDLER:(*state).ixtext, TYPE:0, OFFSET:0l, CH:0b } WIDGET_CONTROL, (*state).ixtext, SET_VALUE=topic list_search_ev, {WIDGET_TEXT_CH, ID:(*state).ixtext, TOP:(*state).tbase, $ HANDLER:(*state).ixtext, TYPE:0, OFFSET:STRLEN(topic)-1, CH:0b } RETURN ENDIF ; Define the list of books in the IDL doc set booknames = [ "Building IDL Applications", $ "DataMiner Guide", $ "External Development Guide", $ "Getting Started", $ "Image Processing", $ "Installation", $ "Intro to ION", $ "ION Java User's Guide", $ "ION Quick Reference", $ "ION Script User's Guide", $ "iTool Developer's Guide", $ "iTool User's Guide", $ "Medical Imaging in IDL", $ "Master Index", $ "Obsolete Features", $ "Online Guide", $ "Quick Reference", $ "Reference Guide", $ "Scientific Data Formats", $ "Using IDL", $ "Wavelet Toolkit User's Guide", $ "What's New in IDL"] ; Define the list of books in the IDL doc set. Note that ; this list of abbreviations is matched with the names of ; PDF files in the PickBook function. book_abr = ['Bld', 'DM', 'EDG', 'GS', 'Img', 'Inst', 'IonI', $ 'IonJ', 'IonQ', 'IonS', 'ITD', 'ITU', 'Med', 'MIDX', 'Obs', $ 'Onlg', 'Quik', 'Ref', 'SDF', 'Use', 'Wav', 'WN'] ; Load bookmarks from file or defaults bkmarks = GetBookmarks(BOOKFILE=bkfile, PREFS=prefs) ; If we got an exact match of a named destination AND ; the user preference says don't show this UI if we got ; an exact match, return. IF ((exact EQ 1) && (prefs.show_always EQ 0)) THEN RETURN ; Find the Index file file = FILEPATH('mindex.txt', SUBDIR=['help']) ; Get the info structure on the selected file fileInfo = FILE_INFO(file) ; If file exists and is readable, show the index. IF fileInfo.read THEN BEGIN ; Create an array to hold the index nlines=LONG(FILE_LINES(file)) index=strarr(nlines) ; Create an array to hold the index of the first element ; in the 'index' array that starts with each ASCII character. itable=LONARR(256) itable[*]=-1 itable[255]=nlines ; Open the file OPENR, unit, file, /GET_LUN, ERROR=err ; Exit if error occurs IF (err NE 0) THEN BEGIN void = DIALOG_MESSAGE(!ERROR_STATE.MSG) RETURN ENDIF ; Read the contents of the file into the 'index' array and ; populate the 'itable' array with the index into the 'index' ; array for the first line that contains each letter or symbol. ; (The 'itable' array is used in the list_search_ev routine.) line='' FOR i = 0l, nlines-1 DO BEGIN READF, unit, line index[i] = line letter = BYTE(STRLOWCASE(STRMID(line,0,1))) IF (itable[letter] EQ -1) THEN itable[letter]=i ENDFOR ; Close the file FREE_LUN, unit ; Create the widgets ; ; First create the top-level base and common controls tbase=WIDGET_BASE(/COLUMN, $ XSIZE=prefs.width, YSIZE=prefs.height, $ XOFFSET=prefs.xpos, YOFFSET=prefs.ypos, $ TLB_FRAME_ATTR=2, $ /TLB_SIZE_EVENTS, $ /TLB_ICONIFY_EVENTS, $ /TLB_MOVE_EVENTS, $ /TLB_KILL_REQUEST_EVENTS, $ /TRACKING_EVENTS, $ /KBRD_FOCUS_EVENTS, $ TITLE='IDL Documentation Index') tbase2=WIDGET_BASE(tbase, /ROW, XPAD=3, /ALIGN_RIGHT) bHelp = WIDGET_BUTTON(tbase2, VALUE=' Help ', TOOLTIP='What is this?', $ EVENT_PRO='help_ev') wTab = WIDGET_TAB(tbase, EVENT_PRO='tab_change_ev') ; Next the index-view widgets ixbase=WIDGET_BASE(wTab, TITLE=' Index ', /COLUMN) ixbase2=WIDGET_BASE(ixbase, /ROW) ixLabel=WIDGET_LABEL(ixbase2, VALUE='Enter a search string') ixtext=WIDGET_TEXT(ixbase, YSIZE=1, $ EVENT_PRO='list_search_ev', /EDIT, /ALL_EVENTS) ixlist=WIDGET_TEXT(ixbase, YSIZE=25, $ EVENT_PRO='list_select_ev', /SCROLL, /ALL_EVENTS) prefbase=WIDGET_BASE(ixbase, /NONEXCLUSIVE) showpref=WIDGET_BUTTON(prefbase, VALUE='Always Show This List', $ EVENT_PRO='showpref_ev') ; Now the bookmark-view widgets bkbase = WIDGET_BASE(wTab, TITLE=' Bookmarks ', /COLUMN) label = WIDGET_LABEL(bkbase, VALUE='Select a bookmark', /ALIGN_LEFT) bklist = WIDGET_TEXT(bkbase, /ALL_EVENTS, /SCROLL, $ VALUE=bkmarks, YSIZE=3, EVENT_PRO='list_select_ev') bkbase2 = WIDGET_BASE(bkbase, /COLUMN, YPAD=10) label = WIDGET_LABEL(bkbase2, VALUE='Manage Bookmarks', /ALIGN_LEFT) bkbase3 = WIDGET_BASE(bkbase2, /COLUMN, /FRAME, /ALIGN_CENTER) bkbase4 = WIDGET_BASE(bkbase3, /ROW) bkLabel1 = WIDGET_LABEL(bkbase4, VALUE='Bookmark Text: ') bkText = WIDGET_TEXT(bkbase4, /EDITABLE, /ALL_EVENTS, $ XSIZE=100, EVENT_PRO='nothing_ev') bkBooks = WIDGET_DROPLIST(bkbase3, TITLE='Book: ', VALUE=booknames, $ EVENT_PRO='nothing_ev') bkbase5 = WIDGET_BASE(bkbase3, /ROW) label = WIDGET_LABEL(bkbase5, VALUE='Page number: ') bkPnum = WIDGET_TEXT(bkbase5, XSIZE=6, /EDITABLE, /ALL_EVENTS, $ EVENT_PRO='nothing_ev') abkbase = WIDGET_BASE(bkbase3, /ROW, /ALIGN_CENTER) bAdd = WIDGET_BUTTON(abkbase, VALUE=' Add ', $ EVENT_PRO='AddBookmark') pad = WIDGET_BASE(abkbase, XSIZE=20) bEditBk = WIDGET_BUTTON(abkbase, VALUE=' Edit ', $ EVENT_PRO='EditBookMark') pad = WIDGET_BASE(abkbase, XSIZE=20) bDelBk = WIDGET_BUTTON(abkbase, VALUE=' Delete ', $ EVENT_PRO='DeleteBookMark') ; Finally, the bottom row of common controls xbase3=WIDGET_BASE(tbase, /ROW) xbase3a=WIDGET_BASE(xbase3, /COLUMN, /ALIGN_LEFT) xbase3b=WIDGET_BASE(xbase3, /COLUMN, /ALIGN_RIGHT) bDone=WIDGET_BUTTON(xbase3a, VALUE=' Done ', EVENT_PRO='Done_ev') bDisplay = WIDGET_BUTTON(xbase3b, VALUE=' Display ', EVENT_PRO='Go_ev') ; Populate the state structure st = { list:PTR_NEW(index, /NO_COPY), $ page:0L, $ book:'', $ offset:0L, $ line:[0L], $ linebegin:0L, $ selection:[0L,0L], $ clicks:0L, $ show_always:prefs.show_always, $ itable:itable, $ tbase:tbase, $ hasFocus:0, $ wTab:wTab, $ ixbase:ixbase, $ ixbase2:ixbase2, $ ixtext:ixtext, $ ixlist:ixlist, $ prefbase:prefbase, $ book_abr:book_abr, $ bkfile:bkfile, $ bklist:bklist, $ bkbase3:bkbase3, $ bkbase4:bkbase4, $ bkLabel1:bkLabel1, $ bkText:bkText, $ bkBooks:bkBooks, $ bkPnum:bkPnum, $ bkmarks:PTR_NEW(bkmarks, /NO_COPY), $ ccFlag:0, $ xpos:prefs.xpos, $ ypos:prefs.ypos, $ width:prefs.width, $ height:prefs.height } state = PTR_NEW(st, /NO_COPY) ; Populate the text widget with the index WIDGET_CONTROL, ixlist, SET_VALUE=*(*state).list, /NO_COPY ; Store the state structure WIDGET_CONTROL, tbase, SET_UVALUE=state ; Realize the widgets WIDGET_CONTROL, tbase, MAP=0 WIDGET_CONTROL, tbase, /REALIZE ; Set the height of the bookmark list to fit the tab. tabheight = (WIDGET_INFO(wTab, /GEOMETRY)).scr_ysize bkbheight = ((WIDGET_INFO(bkbase, /GEOMETRY))).scr_ysize bkb3height = (WIDGET_INFO(bkbase3, /GEOMETRY)).scr_ysize bklheight = tabheight - bkb3height WIDGET_CONTROL, bklist, SCR_YSIZE=bkbheight bkbheight = (WIDGET_INFO(bkbase, /GEOMETRY)).scr_ysize ; Set the height of the index list to fit the tab. ixbheight = (WIDGET_INFO(ixbase, /GEOMETRY)).scr_ysize prefheight = (WIDGET_INFO(prefbase, /GEOMETRY)).scr_ysize ixlheight = tabheight - prefheight WIDGET_CONTROL, ixlist, SCR_YSIZE=ixlheight ; Populate the text entry field WIDGET_CONTROL, ixtext, SET_VALUE=topic, /INPUT_FOCUS ; Set the checkmark in the preferences button WIDGET_CONTROL, showpref, SET_BUTTON=prefs.show_always ; Send an inital event to force the list to update if ; a topic was supplied. list_search_ev, {WIDGET_TEXT_CH, ID:ixtext, TOP:tbase, HANDLER:ixtext, $ TYPE:0, OFFSET:0l, CH:0b } ; Retrieve the "real" geometry and update the state structure tbase_g = WIDGET_INFO(tbase, /GEOMETRY) (*state).width = tbase_g.scr_xsize - (tbase_g.xpad*2) (*state).height = tbase_g.scr_ysize - (tbase_g.ypad*2) ; Send an event to force the widget sizes to update. online_help_pdf_index_event, {WIDGET_BASE, ID:tbase, TOP:tbase, $ HANDLER:tbase, X:0, Y:0 } ; Run XMANAGER XMANAGER, 'online_help_pdf_index', tbase, /NO_BLOCK ; Map the base WIDGET_CONTROL, tbase, MAP=1 ; Set focus to the text input widget WIDGET_CONTROL, ixtext, /INPUT_FOCUS ENDIF ELSE BEGIN ; If index file is missing... void = DIALOG_MESSAGE(['Index file not found or not readable.', $ ' ', 'See ONLINE_HELP_PDF_INDEX in the IDL Reference Guide', $ 'for information.']) ENDELSE END ;---------------------------------------------------------------------