;+ ; NAME: ; DISPLAY ; ; VERSION: ; 1.1 (multiple modifications added to the original 1.0) ; ; PURPOSE: ; The purpose of this routine is to provide transparent replacement for PLOT ; and OPLOT routines. Operates in 2D space. ; ; AUTHOR: ; Pavel A. Romashkin, Ph. D. ; ; CATEGORY: ; Graphics. ; ; CALLING SEQUENCE: ; DISPLAY, [X], [Y], [/OVER], [KEYWORDS_TO_PLOT_COMMAND], [KEYWORDS_TO_IDLgrPlot] ; ; INPUTS: ; Optional X and/or Y, vectors to be displayed. ; ; KEYWORD PARAMETERS: ; Accepts all IDLgrPlot keywords at the time of creation of the plot or with NAME and OVER keywords set. ; Accepts all IDLgrText and IDLgrLegend keywords. Use with /OVER and /LEGEND, or when setting TEXT. ; Accepts standard PLOT keywords but is not guaranteed to honor them all. ; BACKGROUND: set to three-point vector, RGB, in the form [255, 255, 255]. Defaults to white. ; COLOR: set to three-point vector, RGB, in the form [0, 255, 0]. Defaults to black. Will act on the item currently ; being added, or on the named item specified along with COLOR. ; FONT: set to font name, in the form 'Helvetica' (default). ; GROUP_LEADER: set ta valid widget ID to use it as a geroup leader for Display. ; HIDE: use to hide or display a named plot or other named item. ; KEEP_SCALE: set when adding a plot to prevent autoscaling. To rescale to full range later, use double-click. ; LEGEND: will produce a self-updating legend. Use with /OVER. ; LEGEND_TITLE: set to a valid string containing a title for the legend. Use with /OVER. ; LINESTYLE: 0 through 5 as for PLOT, additionally Linestyle=6 for No line. ; LINK: set to a value >0 but <255. This value is used to identify linked windows. If two or more DISPLAY ; windows have the same LINK value and contain plots with the same NAMEs, a ROI drawn to one window ; will cause ROI selection in other linked windows. ROI is drawn using the middle mouse button. ; ROI plots can be removed from DISPLAYs by right-double-clicking on them. ; NAME: optional name for the plot being added. Later, a modifying command with OVER keyword set ; can be used to modify that plot's appearance, or hide it. ; OREF: returns the object reference to the Display window. Use OREF when accessing a Display window ; created as a child in a widget hierarchy. ; OVER: set to apply changes to an existing topmost Display window. ; PARENT: set to valid Widget_base ID. Will cause Display to appear as a child of that base. Use with OREF keyword. ; PRINTOUT: set to print the newly created Display window. Use with OVER to print existing topmost window. ; RESTORE_FILE: set to a string containing the name of a file where a DISPLAY was saved, to restore that DISPLAY. ; SAVE: set this keyword to save contents of DISPLAY to a file. Use with optional FILENAME keyword. ; SET_ROI_COLOR: set to three-point vector, RGB, to specify a ROI color. Defaults to red. ; TEXT: set to a valid string or string array. Most formatting keywords for the text (object) will be also honored. ; DATA_SCALE: use with TEXT to specify position in data coordinates. Dragging data scaled text will produce ; unexpected results. ; T_NAME: set to a string to identify a particular text block. ; KILL: use with T_NAME to delete a named text block programmatically. Right_double_click will delete, too. ; VERT_COLOR: color matrix bytarr(3, n_elements(x)) describing RGB colors of each vertex of the plot. ; Symbols will be of the color set by COLOR keyword. ; WTITLE: set to a string containing (short) text for window title. ; ; OBJECT METHODS: ; Unless listed below, transparent to the end user. See individual routines for functionality description. ; GET_DATA: invoke this function method on an instance of PARgrDisplay returned by OREF keyword. The result ; is a structure which fields 1 through X are FLTARR(3, *) of plots data from the object. ; ; COMMON BLOCKS: ; PARgrDisplay_top_window, Temp. One common variable, type - object reference. ; Used to keep track of the topmost Display window so that a user can create and ; modify several of them. ; ; SIDE EFFECTS: ; None. ; ; RESTRICTIONS: ; Designed under IDL 5.3. Version-specific features include: ; COMPILE_OPT calls. In earlier versions of IDL, comment these lines out. ; Some keyword parameters are passed undefined. IDL 5.3 allows that. ; Earlier versions of IDL may require checking for validity of variables. ; ; EXAMPLE: ; Creating a new Display window (plots can be appended): ; DISPLAY ; ; Creating a new Display with existing data: ; DISPLAY, sin(findgen(100)*0.1)+1.0, psym=4, linestyle=6, color=[255,0,0], $ ; xtitle='My X axis', ytitle='My Y axis', title='My nice plots', $ ; name='plot_1', wtitle='Top window' ; ; Appending a plot to the existing topmost Display window and making the window linked using the ID of 1: ; DISPLAY, sin(findgen(100)*0.15)+0.9, psym=5, color=[0,0,200], LINK=1, /OVER ; ; Changing properties of the existing topmost Display window: ; DISPLAY, background=[200,200,0], title='New plot title', name='plot_1', $ ; color=[200,0,0], /OVER ; ; PROCEDURE: ; Display will create and maintain automatic ranges for the window. When a plot ; is added that expands the range, or a plot is removed that had the largest range, ; the window can re-scale to accomodate the largest present plot. Use /KEEP_SCALE ; to prevent autoscaling. ; Legend and / or Text items can be freely moved using dragging with the right mouse button, and ; can be deleted using right-double-click. ; Zooming is performed by Left-mouse-drag in the window. Full zoom-out is performed ; using Left-double-click in the window. ; Any item, including plots and axes, can be removed from Display by Right-double-clicking on that plot. ; No "undo" functionality is provided. ; Multiple windows are allowed to be active at the same time. Another plot can be ; added to any window by bringing it to the foreground (clicking on it with the mouse) ; and executing Display, . . . , /OVER command. ; Co-existing windows can interact with each other through the use of LINK keyword parameter. ; Windows with the same, non-zero LINK value and containing the same plot NAMEs, will produce ; a region of interest (ROI) highlight if a ROI is drawn in one of such linked windows. Use middle ; mouse button to draw a ROI. ; LINK can be set to another value at any time using /OVER keyword. ; Formatting commands can be issued to an existing Display window without adding ; new plots by using Display, . . . , /OVER command. ; Programmatically, can be used as a child to any base widget in any widget program ; (refer to PARENT, OREF and GROUP_LEADER keywords). ; ; EXPANDABILITY: ; Right, middle and left mouse buttons can be used while Single, Double clicking or ; dragging. Different color ROI selection rectangles are provided to indicate action. ; Supplying user procedures allows to perform various actions on the data in ROI. ; ; MODIFICATION HISTORY: ; Written: P.A.Romashkin, 08-2000 ; Documented, P.A.Romashkin, 8-2000 ; GET_DATA, SAVE and RESTORE added, PAR, 12-2000 ; Documented additions, P.A.Romashkin, 12-2000 ; Separated mouse events into object methods and added DATA_SCALE to TEXT, PAR, 8-2001 ;- ; ************************************************************************* ; Provides event handling for the top level widget and draw window. pro Display_event, event compile_opt IDL2, OBSOLETE Common PARgrDisplay_top_window, got_focus ; Provide error handling. Catch, Err_code if (Err_code ne 0) then begin print,'PARgrDisplay_event error caught:' help, /last_message catch, /cancel return endif ; Get object reference out, as it is used virually everwhere. widget_control, event.id, get_uval=Object ; ---------------------------- THESE ARE TOP BASE EVENTS ---------------------------- ; Now, handle the possibility of focusing event. User will be able to Oplot to existing window. ; Common block is used to store the information about topmost window as it gains focus. if tag_names(event, /STRUCTURE_NAME) EQ 'WIDGET_KBRD_FOCUS' then begin if (event.enter eq 1) then begin ; This means Display window was brought to the top. if n_elements(got_focus) eq 0 then begin ; If this is the first object ever, reset GOT_FOCUS. got_focus = object return endif if not obj_valid(got_focus[0]) then begin ; If the first object is invalid, replace it. got_focus[0] = object got_focus = got_focus[where(obj_valid(got_focus))] return endif if got_focus[0] eq object then return ; Don't waste time if it was on top already. index = where(got_focus eq object, count) ; Locate the object that was brought up in the array... if count gt 0 then got_focus[index] = obj_new() ; and invalidate the existing reference to it. got_focus = got_focus[where(obj_valid(got_focus))] ; Now, got_focus does NOT contain the Object reference. Add it: if got_focus[0] -> is_linked() eq 0 then got_focus[0] = object else got_focus = [object, got_focus] ; Now, common block has top Display at 0 position, and Linked Displays in other positions of got_focus. endif return endif ; Lastly, it may be resize event. Change the size of daughter Draw widget. if tag_names(event, /STRUCTURE_NAME) EQ 'WIDGET_BASE' then begin widget_control, widget_info(event.id, /child), draw_xsize=event.x, draw_ysize=event.y Object -> setProperty, /draw return endif ; ---------------------------- END OF TOP BASE EVENTS ---------------------------- ; Process mouse events. Use all options of Single or Double click, drag and release with three mouse buttons. case event.type of 0: object -> mouse_press, event ; Mouse press 1: object -> mouse_release, event ; Mouse release 2: object -> mouse_drag, event ; Mouse drag endcase end ; EVENT HANDLING PROCEDURE ;************************************************************************* ; Method called on mouse press in the DISPLAY window. Helps structure event handling procedure. pro PARgrDisplay::mouse_press, event compile_opt IDL2, OBSOLETE if event.clicks eq 2 then begin ; Process double-click events. case event.press of 1: begin ; Left button double-click zooms fully out. self -> getProperty, xrange=xrange, yrange=yrange, obj_win=obj_win, Plot_list = temp, $ view = view temp = temp[0] if obj_valid(temp) then temp -> zoom, obj_win, xrange=xrange, yrange=yrange, $ ; Zoom out. norm_x=norm_x, norm_y=norm_y, /draw self -> setProperty, norm_x=norm_x, norm_y=norm_y ; Remember new normalization. end 2: begin ; Middle-double-click is for user added functionality. self -> getProperty, obj_win=obj_win, plot_list=plot_list, view=view ; The following prints the coordinates of the double click. ;temp = obj_win -> pickData(view, plot_list[0], [event.x, event.y], coords) ;print, 'X coord.: ', coords[0], ' Y coord.: ', coords[1] ; The following is optional. It prints out plot name, if double click hit a plot. temp = obj_win -> select(view, [event.x, event.y]) if obj_valid(temp[0]) then begin temp[0] -> getProperty, name=name print, name endif end 4: begin ; Right-double-click kills selected plot. self -> killplot, mpress=[event.x, event.y], /draw end else: endcase endif else begin ; Single click establishes the starting point for ROI or zooming rectangle. mpress = [event.x, event.y] self -> setproperty, Mpress = mpress, mouse_button=event.press self -> getproperty, norm_x=n_xr, norm_y=n_yr, second_layer=second_layer ; Get coordinate conversions. case event.press of 1: begin ; This is for zooming, using left mouse button. widget_control, event.id, /draw_motion ; Turn on motion events. temp = obj_new('IDLgrPolygon', color=[0, 200, 0], linestyle=0, style=1, $ xcoord_conv=n_xr, ycoord_conv=n_yr) ; Use data coordinate system. self -> setProperty, roi=temp ; Store the polygon in ROI field and add it to the model. end 2: begin ; Use this event to create ROI object. widget_control, event.id, /draw_motion ; Turn on motion events. geom = widget_info(event.id, /geometry) x0 = (Mpress[0]*1.12/geom.draw_xsize-0.1 - n_xr[0])/n_xr[1] y0 = (Mpress[1]*1.16/geom.draw_ysize-0.1 - n_yr[0])/n_yr[1] ; These lines are here because of the bug in IDLgrROI. ;In IDL 5.3, only normalized coords were supported. ;********** ; x0 = (Mpress[0]*1.12/geom.draw_xsize-0.1) ; y0 = (Mpress[1]*1.16/geom.draw_ysize-0.1) ;********** ; If self is not linked, allow drawing of ROI but make it dotted. if self -> is_linked() eq 0 then begin linestyle=1 temp = obj_new('IDLgrText', 'This window is not linked with others.', location=[0.3, 0.5], $ color=[255,0,0]) second_layer -> add, temp ; Add the text to the window. self -> setProperty, trash=temp endif else linestyle=0 roi = obj_new('IDLgrRoi', x0, y0, xcoord_conv=n_xr, ycoord_conv=n_yr, linestyle=linestyle) self -> setProperty, roi=roi, /draw ; Store the ROI in roi field. end 4: begin ; Right mouse click and drag is used to move objects on Display. ; Drag events are not needed for moving objects, just as nothing else is needed. ; All action is performed on mouse release. end else: endcase endelse end ;************************************************************************* ; Method called on mouse release in the DISPLAY window. Helps structure event handling procedure. pro PARgrDisplay::mouse_release, event compile_opt IDL2, OBSOLETE widget_control, event.id, draw_motion=0 ; Turn motion events off. self -> getProperty, Plot_list=Plot_list, Mpress=Mpress, obj_win=obj_win, model=model, view=view, $ norm_x=n_xr, norm_y=n_yr case event.release of 1: begin ; This release event is left mouse button and will zoom in. Plot_list = Plot_list[0] if obj_valid(Plot_list) then Plot_list -> zoom, obj_win, mpress, [event.x, event.y], $ norm_x=norm_x, norm_y=norm_y, /draw ; Apply Zoom. self -> setProperty, norm_x=norm_x, norm_y=norm_y, roi=0, /draw ; Kill ROI object. end 2: begin ; This event is middle mouse button and will select ROI. self -> getproperty, roi=roi ; If active window is not linked, clean up and leave. if self -> is_linked() eq 0 then begin self -> setProperty, roi=0, trash=0, /draw return endif if not obj_valid(roi) then return ; These lines are here because of the bug in IDLgrROI. ROI in 5.3 is drawn in the right place but ; uses totally wrong data space. Replace data with correct set before aquiring data from plots. ;********** ; x = (data[0, *]-n_xr[0])/n_xr[1] ; because IDLgrROI uses normalized coordinates because of the bug, ; y = (data[1, *]-n_yr[0])/n_yr[1] ; replace ROI with the one in data coordinates. ; roi -> setProperty, /hide ; ROI will be off-scale. Hide it. ; roi -> replacedata, x, y ;********** self -> link_roi, roi self -> setProperty, roi=0, /draw ; Kill ROI object. end 4: begin selected_id = (obj_win -> select(view, mpress))[0] if not obj_valid(selected_id) then return ; All text is located on Second_layer. That model does not use data coordinates. geom = widget_info(event.id, /geometry) x0 = (Mpress[0]*1.12/geom.draw_xsize-0.1) y0 = (Mpress[1]*1.16/geom.draw_ysize-0.1) x1 = (event.x*1.12/geom.draw_xsize-0.1) y1 = (event.y*1.16/geom.draw_ysize-0.1) ; If a legend was selected, use its Translate method in normalized coordinates. if obj_class(selected_id) eq 'IDLGRLEGEND' then begin delta_x = x1 - x0 delta_y = y1 - y0 selected_id -> translate, delta_x, delta_y, 0 endif else $ ; If it was a text object, use its Location property. if obj_class(selected_id) eq 'IDLGRTEXT' then begin selected_id -> getProperty, location=loc, strings=strings n_lines = n_elements(strings) ; Keep row spacing for multiple rows. if n_lines eq 1 then loc = [x1, y1] else begin offset = (loc[1, 0:1]-shift(loc[1, 0:1],1))[1] ; Obtain row spacing... loc[0, *] = x1 loc[1, *] = indgen(n_lines)*offset + y1 ; ...and apply this same spacing at new location. endelse selected_id -> setProperty, location=loc endif self -> draw end else: endcase end ;************************************************************************* ; Method called on mouse drag in the DISPLAY window. Helps structure event handling procedure. pro PARgrDisplay::mouse_drag, event compile_opt IDL2, OBSOLETE self -> getProperty, Mpress=Mpress, view=view, obj_win=obj_win, roi=roi, $ xrange=xrange, yrange=yrange, mouse_button=mouse_button, norm_x=n_xr, norm_y=n_yr ; This is needed to convert manually mouse screen coordinates to data coordinates for ROI, ; and for Zoom rectangle. geom = widget_info(event.id, /geometry) ; Since the viewplane_rect is from -0.1, -0.1 and is 1.12 by 1.16, correct for these. Convert the ; window coordinates to data coordinates. x0 = (Mpress[0]*1.12/geom.draw_xsize-0.1 - n_xr[0])/n_xr[1] ; This is for mouse press. x1 = (event.x*1.12/geom.draw_xsize-0.1 - n_xr[0])/n_xr[1] ; This is for drag coordinate. y0 = (Mpress[1]*1.16/geom.draw_ysize-0.1 - n_yr[0])/n_yr[1] ; This is for mouse press. y1 = (event.y*1.16/geom.draw_ysize-0.1 - n_yr[0])/n_yr[1] ; This is for drag coordinate. case mouse_button of 1: begin ; Update the zooming rectangle. if not obj_valid(roi) then return ; On some fast (poorly controlled) drags, a ROI does not get established. roi -> setProperty, data=[[x0, y0], [x0, y1], [x1, y1], [x1, y0], [x0, y0]] self -> draw end 2: begin ; Update ROI object. x1 = (event.x*1.12/geom.draw_xsize-0.1 - n_xr[0])/n_xr[1] y1 = (event.y*1.16/geom.draw_ysize-0.1 - n_yr[0])/n_yr[1] ; These lines are here because of the bug in IDLgrROI in IDL 5.3. ;********** ; x1 = (event.x*1.12/geom.draw_xsize-0.1) ; y1 = (event.y*1.16/geom.draw_ysize-0.1) ;********** roi -> appendData, x1, y1 self -> draw end 4: begin end else: endcase end ;************************************************************************* ; Standard object property retrieval method. pro PARgrDisplay::getProperty, view=view, Obj_win=Obj_win, mouse_button=mouse_button, Mpress=Mpress, $ Plot_list=Plot_list, Axes_list=Axes_list, xrange=xrange, yrange=yrange, symbol_list=symbol_list, $ title=title, xtitle=xtitle, ytitle=ytitle, top_ID=top_ID, model=model, roi=roi, uvalue=uvalue, $ second_layer=second_layer, link=link, norm_x=norm_x, norm_y=norm_y, get_data=get_data compile_opt IDL2, OBSOLETE top_ID = self.top_ID model = self.container -> get(position=0) second_layer = self.container -> get(position=1) view = self.view link = self.link Obj_win = self.Obj_win roi = self.roi mouse_button = self.mouse_button Mpress = self.Mpress Plot_list = self.Plot_list -> get(/all) Axes_list = self.Axes_list -> get(/all) xrange = self.xrange yrange = self.yrange symbol_list = self.symbol_list -> get(/all) title = self.title -> get(position=0) xtitle = self.title -> get(position=1) ytitle = self.title -> get(position=2) norm_x = self.norm_x norm_y = self.norm_y if n_elements(*self.uvalue) ne 0 then uvalue = *self.uvalue else uvalue = 0b end ;************************************************************************* ; Standard object property definition method. pro PARgrDisplay::setProperty, title=title, xtitle=xtitle, ytitle=ytitle, mpress=mpress, link=link, $ mouse_button=mouse_button, draw=draw, background=background, name=name, uvalue=uvalue, $ wtitle=wtitle, roi=roi, norm_x=norm_x, norm_y=norm_y, legend=legend, text=text, trash=trash, $ set_roi_color=set_roi_color, psym=psym, rename=rename, kill=kill, polygon=polygon, _extra=_extra compile_opt IDL2, OBSOLETE if n_elements(title) gt 0 then (self.title -> get(position=0)) -> setProperty, string = title if n_elements(link) ne 0 then self.link = link if n_elements(xtitle) gt 0 then (self.title -> get(position=1)) -> setProperty, string = xtitle if n_elements(ytitle) gt 0 then (self.title -> get(position=2)) -> setProperty, string = ytitle if n_elements(mpress) gt 0 then self.mpress = mpress if n_elements(norm_x) gt 0 then self.norm_x = norm_x if n_elements(norm_y) gt 0 then self.norm_y = norm_y if n_elements(mouse_button) gt 0 then self.mouse_button = mouse_button if n_elements(uvalue) gt 0 then *self.uvalue = uvalue if n_elements(background) eq 3 then (self.container -> get(position=1)) -> setProperty, color=background if n_elements(set_roi_color) ne 0 then self.roi_color = set_roi_color if keyword_set(legend) then self -> legend, /update, _extra=_extra if n_elements(text) ne 0 then self -> text, text, kill=kill, _extra=_extra if n_elements(polygon) ne 0 then self -> polygon, polygon, _extra=_extra if n_elements(wtitle) ne 0 then begin widget_control, self.top_id, tlb_set_title=wtitle self.wtitle = wtitle endif if n_elements(roi) ne 0 then begin ; Use this to set or kill the ROI object. If set not to obj. ref., destroy the ROI. obj_destroy, self.roi if obj_valid(roi) then begin ; Add the ROI object to the model for displaying. self.roi=roi if obj_class(roi) eq 'IDLGRROI' then roi -> setProperty, color=self.roi_color ;roi -> setProperty, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y model = self.container -> get(position=0) model -> add, roi endif endif if n_elements(trash) ne 0 then begin ; Use this to add or empty the trash object. If set not to obj. ref., empty trash. trashed_items = self.trash -> get(/all, count=count) if count gt 0 then obj_destroy, trashed_items if obj_valid(trash) then self.trash -> add, trash endif if n_elements(name) ne 0 then begin model = self.container -> get(position=0) target_plot = model -> getByName(name) ; If PSYM was specified, modify symbols. Else, modify plot itself. if obj_valid(target_plot) then begin ; If plot valid if size(rename, /type) eq 7 then target_plot -> setProperty, name=rename ; Allow to rename the plot if n_elements(psym) ne 0 then begin target_plot -> getProperty, symbol=symbol_used if obj_valid(symbol_used) then symbol_used -> setProperty, data=psym, _extra=_extra $ else begin symbol_used = obj_new('IDLgrSymbol', psym, _extra=_extra) self.container -> add, symbol_used target_plot -> setProperty, symbol=symbol_used endelse endif $ else target_plot -> setProperty, _extra=_extra ; If PSYM was specified self -> rescale, /keep_scale ; Rescale in case plot data were changed if keyword_set(kill) then obj_destroy, target_plot endif else print, 'No plot with such name.' endif if keyword_set(draw) then self.obj_win -> draw, self.view end ;************************************************************************* ; Plotting method for the PARgrDisplay. Used for both initial plotting and overplotting. ; Some part of this procedure are redundant with RESCALE method, but it is more efficient here ; not to call RESCALE, because there is no need to query all Plots, as all ranges are available already. pro PARgrDisplay::oplot, x_in, y_in, psym=psym, xrange=xrange, yrange=yrange, draw=draw, $ keep_scale=keep_scale, _extra=_extra compile_opt IDL2, OBSOLETE if n_elements(y_in) eq 0 then begin x = indgen(n_elements(x_in)) ; Generate abscissa if it is missing. y = x_in endif else begin ; Isolate the parameters from internal data used to make a plot. x = x_in y = y_in endelse ; Always store full ranges for zooming out to right full range. xrange_calc = get_range(x) yrange_calc = get_range(y) ; If this is the first plot, replace ranges altogether. if self.plot_list -> count() eq 0 then begin self.xrange = xrange_calc self.yrange = yrange_calc endif else begin ; Otherwise, choose the wider ranges. self.xrange = [self.xrange[0] < xrange_calc[0], self.xrange[1] > xrange_calc[1]] self.yrange = [self.yrange[0] < yrange_calc[0], self.yrange[1] > yrange_calc[1]] endelse ; Check if we want to keep a Zoom-in. if keyword_set(keep_scale) then begin ; Get axes to obtain current display ranges from... x_axis = self.axes_list -> get(position=0) y_axis = self.axes_list -> get(position=1) ; ...and get current ranges from axes to use for overplotting. x_axis -> getProperty, xrange=xrange y_axis -> getProperty, yrange=yrange endif else begin ; If not retaining Zoom, use either provided or automatic ranges.. if not keyword_set(xrange) then xrange = self.xrange if not keyword_set(yrange) then yrange = self.yrange endelse if n_elements(psym) ne 0 then symbol = obj_new('IDLgrSymbol', psym, _extra=_extra) add_plot = obj_new('IDLgrPlot', x, y, symbol=symbol, _extra=_extra) model = self.container -> get(position=0) model -> add, add_plot, position=0 ; Bring Display to scale and retrieve new normalization vectors. add_plot -> zoom, self.obj_win, xrange=xrange, yrange=yrange, norm_x=norm_x, norm_y=norm_y, /draw ; Norm_X and Norm_Y are defined now. Store them in Self. self.norm_x = norm_x self.norm_y = norm_y ; Need to change *norm_coord for text objects in the model. objects_list = model -> get(isa='IDLGRTEXT', /all, count=count) if count gt 1 then for i=1, count-1 do objects_list[i] -> setProperty, xcoord_conv=norm_x, ycoord_conv=norm_y ; Update the display. self.plot_list -> add, add_plot if obj_valid(symbol) then self.symbol_list -> add, symbol else self.symbol_list -> add, obj_new() self -> legend, /update if keyword_set(draw) then self.obj_win -> draw, self.view end ;************************************************************************* ; Adds a polygon to the Display. pro PARgrDisplay::polygon, coords, kill=kill, _extra=_extra compile_opt IDL2, OBSOLETE self -> getProperty, model=model keywords = tag_names(_extra) loc = (where(keywords eq 'P_NAME', count))[0] ; Check if polygon was addressed by name. if count ne 0 then begin polygon = model -> GetByName(_extra.(loc)) if not obj_valid(polygon) then begin polygon = obj_new('IDLgrPolygon', coords, _extra=_extra, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y) model -> add, polygon endif polygon -> setProperty, _extra=_extra, name=_extra.p_name endif else $ if not obj_valid(polygon) then begin polygon = obj_new('IDLgrPolygon', coords, _extra=_extra, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y) model -> add, polygon endif if keyword_set(kill) then obj_destroy, polygon self -> draw end ;************************************************************************* ; Adding a CONTOUR to the plot object. pro PARgrDisplay::contour, data=data compile_opt IDL2, OBSOLETE if n_elements(data) ne 0 then begin temp = obj_new('IDLgrContour', /planar) self -> getProperty, model=model temp -> setProperty, n_levels=20, data_values=data, $ xcoord_conv=self.norm_x, ycoord_conv=self.norm_y model -> add, temp self -> setProperty, trash=temp, /draw endif end ;************************************************************************* ; Updates scaling of the plot if the data on it has changed through IDLgrPlot keywords. pro PARgrDisplay::rescale, keep_scale=keep_scale, draw=draw compile_opt IDL2, OBSOLETE plot_list = self.plot_list -> get(/all, count=count) if count ne 0 then begin plot_list[0] -> getProperty, data=data self.xrange = get_range(data[0, *]) self.yrange = get_range(data[1, *]) endif else return if count gt 1 then $ for i = 1, n_elements(plot_list)-1 do begin plot_list[i] -> getProperty, data=data xrange = get_range(data[0, *]) yrange = get_range(data[1, *]) ; Find new ranges. They may expand the existing ranges, or may not. xrange = [self.xrange[0] < xrange[0], self.xrange[1] > xrange[1]] yrange = [self.yrange[0] < yrange[0], self.yrange[1] > yrange[1]] self.xrange = xrange self.yrange = yrange endfor if not keyword_set(keep_scale) then begin plot_list[0] -> zoom, self.obj_win, xrange=self.xrange, yrange=self.yrange, $ norm_x=norm_x, norm_y=norm_y, draw=keyword_set(draw) ; Store new coord_conv in the object. self.norm_x = norm_x self.norm_y = norm_y endif end ;************************************************************************* ; Removes selected plot from the graph and kills empty references in Containers. pro PARgrDisplay::killplot, plot_id, mpress=mpress, draw=draw compile_opt IDL2, OBSOLETE if keyword_set(mpress) and size(plot_id, /type) ne 11 then begin view = self.view plot_id = (self.obj_win -> select(view, mpress))[0] end ; If something other than a plot was selected, do nothing. if not obj_valid(plot_id) then return if obj_class(plot_id) ne 'IDLGRPLOT' then begin obj_destroy, plot_id if keyword_set(draw) then self.obj_win -> draw, self.view return endif ; Kill the plot and remove it from the container. plot_id -> getProperty, symbol=symbol self -> getProperty, plot_list=plot_list, symbol_list=symbol_list loc = (where(plot_id eq plot_list))[0] ; Remove plot and symbol from their containers. self.plot_list -> remove, plot_id self.symbol_list -> remove, position = loc ; Destroy these objects. if obj_valid(symbol) then obj_destroy, symbol obj_destroy, plot_id ; Need to update ranges, in case the plot that's removed was affecting the limits of the axes. self -> rescale, /keep_scale ; Leave zoom at its current level. self -> legend, /update if keyword_set(draw) then self.obj_win -> draw, self.view end ;************************************************************************* ; Will return link value for the object. function PARgrDisplay::is_linked compile_opt IDL2, OBSOLETE return, self.link end ;************************************************************************* ; This method links several DISPLAYs that have the same LINK flag value, using a plot that has a name ; 'LINK' and supplied region of interest index. I can't come up with how exactly to link DISPLAYs if they ; are used inside widget programs, so I leave LINK only to independent windows. ; Some loops are present here. They are quite short and result from operations on object arrays. pro PARgrDisplay::link_roi, roi compile_opt IDL2, OBSOLETE Common PARgrDisplay_top_window, obj_list ; Get list of plots from the window that initiated the ROI link. self -> getProperty, plot_list=plot_list, link=link_id ; If plot is not linked, exit quietly. if link_id eq 0 then return ; Get information about plots in the window that initiated ROI. n_plots = n_elements(plot_list) names = strarr(n_plots) for i=0, n_plots-1 do begin ; Get list of plot names from Display containing ROI. plot_list[i] -> getProperty, name=temp names[i] = temp endfor for outer_loop=0, n_elements(obj_list)-1 do begin ; We will link over all plots having same, non-empty names. obj_list[outer_loop] -> getProperty, plot_list=curr_plot_list, model=model, link=curr_link_id ; Do not look into Display windows that have different link ID. if curr_link_id ne link_id then goto, leave_loop for i=0, n_elements(curr_plot_list)-1 do begin curr_plot_list[i] -> getProperty, name=temp ; Do not get DATA here to reduce memory use. if temp eq '' then goto, leave_loop ; Ignore plots whos name was not explicitly specified. if strpos(temp, 'Linked') ne -1 then goto, leave_loop ; Ignore links, or it may get confusing. loc = where(names eq temp, count) if count eq 0 then goto, leave_loop ; If the name is not common with ROI-initiator, leave. if count gt 1 then message, 'More than one plot named '+temp+' is present.', /informational ; If a plot is already linked, add a number to name's end to help identify different subsets. added_name = where(strpos(names, 'Linked '+temp) ne -1, count) if count gt 0 then begin added_name = names[added_name[count -1]] last_number = strpos(added_name, ' ', /reverse_offset, /reverse_search) > 0 last_number = strmid(added_name, last_number+1) if (byte(last_number))[0] gt 57b then added_name = 'Linked '+temp+' 1' $ else added_name = 'Linked '+temp+' '+strcompress(fix(last_number)+1, /rem) endif else added_name = 'Linked '+temp ; Done adding number to "linked xxxx ". plot_list[loc[0]] -> getProperty, data=data ; Get data from the parent DISPLAY's plot. roi_index = roi -> ContainsPoints(data) ; This is index of points in "LINK" enclosed by ROI. inside_index = where(roi_index gt 0, count) ; Include points inside, on borders and on verteces of ROI. if count eq 0 then goto, leave_loop ; If there are no points inside ROI for this NAME, leave. curr_plot_list[i] -> getProperty, data=data ; Use the same variable to reduce memory use. data = data[*, inside_index] ; Restrict data to ROI index. ; Use DISPLAY to add a plot to the linked window. display, data[0, *], data[1, *], psym=4, color=self.roi_color, linestyle=6, oref=obj_list[outer_loop], /over, $ name=added_name leave_loop: endfor endfor ; outer_loop end ;************************************************************************* ; Procedure for printing the selected Display window. pro PARgrDisplay::printout compile_opt IDL2, OBSOLETE ; Get main PARgrDisplay properties. self -> getProperty, view=view, Obj_win=Obj_win, model=model Printer = obj_new('IDLgrPrinter') ; Create a printer object. printer -> getProperty, dim=prt_dim ; Get dimensions of Obj_win, Screen and Printer to preserve the XY ratio of the object plot. Obj_win -> getproperty, dim=win_dim scr_dim = get_screen_size() ; Take care to preserve PARgrDisplay's X to screen width ratio and aspect ratio. x_ratio = win_dim[0] / scr_dim[0] aspect_ratio = win_dim[1] / win_dim[0] ; Now, scale the plot so that X takes X_RATIO of the printed width and Y is factor of ; ASPECT_RATIO with respect to the new X size. model -> scale, x_ratio, aspect_ratio*x_ratio, 1 ; Ready to print. Allow choosing printing options. ok = dialog_printjob(Printer) if ok then Printer -> draw, view, vector=0 view -> setproperty, location=[0.0, 0.0], dimensions=[0, 0] ; Return the model to the original scale. model -> scale, 1/x_ratio, 1/(aspect_ratio*x_ratio), 1 ; Printer object no longer needed. obj_destroy, Printer end ;************************************************************************* ; Allows to copy the object to the clipboard pro PARgrDisplay::copy, _extra=_extra compile_opt IDL2, OBSOLETE temp = obj_new('IDLgrClipboard', _extra=_extra) temp -> draw, self.view, _extra=_extra obj_destroy, temp end ;************************************************************************* ; Allows to save the current display window, with all the data, for future restoring. pro PARgrDisplay::save, _extra=_extra compile_opt IDL2, OBSOLETE save, self, _extra=_extra end ;************************************************************************* ; This procedure allows to create a window for a restored contents of a previously saved DISPLAY. ; The only two properties of SELF that pertain to GUI are SELF.TOP_ID and SELF.OBJ_WIN, so they will ; need to be replaced with valid references. pro PARgrDisplay::gui compile_opt IDL2, OBSOLETE on_error, 1 if obj_class(self) ne 'PARGRDISPLAY' then return self.top_ID = widget_base(/kbrd_focus_ev, /tlb_size_ev, uvalue=self) if self.wtitle ne '' then widget_control, self.top_ID, tlb_set_title=self.wtitle $ else widget_control, self.top_ID, tlb_set_title='Display window '+strtrim(self.top_ID, 2) ; Draw area is a child of self.top_ID. Restoring is for interactive use only, so Parent etc. is not needed. draw_ID = widget_draw(self.top_ID, graphics=2, /button_event, xsize=self.window_xsize, $ ysize=self.window_ysize, event_pro='Display_event', retain=2, uvalue=self) ; Need to realize it all now, because we can only get value of realized widget_draw in object mode. widget_control, self.top_ID, /realize xmanager, 'display', self.top_ID, /No_block, cleanup='display_cleanup' widget_control, draw_ID, get_value=Obj_Win obj_destroy, self.obj_win ; Delete this invalid reference... self.obj_win = obj_win ; ...replace it with the newly created one... self -> draw ; ... and update the contents of DISPLAY. end ;************************************************************************* ; There also is a DRAW keyword to SetProperty procedure. This one is short and fast and is used ; when drawing is the only thing done at that particular time. pro PARgrDisplay::draw compile_opt IDL2, OBSOLETE self.obj_win -> draw, self.view end ;************************************************************************* ; The following procedure will analyze the plot_list container, get all needed info and create a legend ; in the form of a separate model (IDLgrLegend) that will be moveable etc. pro PARgrDisplay::legend, legend_title=legend_title, kill=kill, update=update, _extra=_extra compile_opt IDL2, OBSOLETE self -> getproperty, second_layer=model ; Check if there is a legend already. legend = model -> get(isa='IDLGRLEGEND', /all) ; Legend is unique for its own set of plots. if obj_valid(legend) and keyword_set(kill) then begin obj_destroy, legend ; Kill the legend if KILL is set. self -> draw return endif if not obj_valid(legend) then begin ; Only add a legend if it is new. if keyword_set(update) then return ; If there was no legend, do not create one. Only update if one existed. legend = obj_new('IDLgrLegend') legend -> setProperty, gap=0.5, glyph_width=3. model -> add, legend endif Plot_list = self.Plot_list -> get(/all, count=count) if count eq 0 then begin ; If the last plot was removed from DISPLAY, remove all legend information but keep the legend there. ; This way, if a user adds a plot to DISPLAY, it will automatically show up in the legend. legend -> setProperty, item_name='', item_object=obj_new() return ; If no plots are present, quit. endif item_color = fltarr(3, count) item_name = strarr(count) item_linestyle = bytarr(count) item_object = objarr(count) for i=0, count-1 do begin ; Get information from all plot objects in DISPLAY. plot_list[i] -> getProperty, color=color, symbol=symbol, linestyle=linestyle, name=name item_color[*, i] = color if name ne '' then item_name[i] = name else item_name[i] = 'Plot '+strcompress(i, /rem) item_linestyle[i] = linestyle if obj_valid(symbol) then item_object[i] = symbol endfor if n_elements(legend_title) ne 0 then begin ; If legend_title was provided, create title object and store it in Container. title_obj = obj_new('IDLgrText', legend_title, _extra=_extra, name='legend_title', recompute=2) self.container -> add, title_obj endif ; Set all properties of the legend according to the currend window contents. legend -> setProperty, item_name=item_name, item_color=item_color, item_linestyle=item_linestyle, $ item_object=item_object, /select_target, title=title_obj, _extra=_extra self -> draw end ;************************************************************************* ; This method will add text to Display object. Text will be moveable using the right mouse button. ; The text is updatable given it was named at the time of its creation. To update the text, pass ; its new value as an argument and set T_NAME keyword to the name of the text block. ; Named text block can also be killed programmatically using /KILL ketword. ; Data_scale keyword will place text into the data coordinates that it is set to. pro PARgrDisplay::text, value, t_name=t_name, kill=kill, data_scale=data_scale, _extra=_extra compile_opt IDL2, OBSOLETE ; Determine the location for the new text right in the middle of Display. n_lines = n_elements(value) if n_lines eq 0 then return ; If TEXT was not provided, do nothing. loc = fltarr(2, n_lines) if n_elements(data_scale) ne 0 then begin self -> getProperty, model=model norm_x = self.norm_x norm_y = self.norm_y endif else begin self -> getProperty, second_layer=model ; Get Model to add Text to. loc[*] = 0.5 ; Set up the location for the new text in the middle of the window. endelse temp = 'void' ; Initialize TEMP with something invalid. if size(t_name, /type) eq 7 then temp = model -> getbyname(t_name) if keyword_set(kill) then begin ; Kill text if requested. obj_destroy, temp return endif ; Creating new text in DISPLAY window. if obj_class(temp) ne 'IDLGRTEXT' then begin ; Create new text object. temp = obj_new('IDLgrText', value, alignment=0.5, recompute=2, $ xcoord_conv = norm_x, ycoord_conv = norm_y, _extra=_extra) ; Create new object... if size(t_name, /type) eq 7 then temp -> setProperty, name=t_name model -> add, temp ; ...add it to second_layer... self.container -> add, temp ; ...add it to Container. if n_elements(data_scale) eq 2 then begin loc[0, *] = data_scale[0] ; Set up the location for the new text. loc[1, *] = data_scale[1] ; Set up the location for the new text. endif if n_lines gt 1 then begin char_dim = self.obj_win -> GetTextDimensions(temp) spacing = char_dim[1]*1.5 loc[1, *] = loc[1, *] - transpose(indgen(n_lines)*spacing) temp -> setProperty, location=reform(loc) endif endif else begin temp -> getProperty, location=former_position, strings=old_contents n_old_strings= n_elements(old_contents) if n_old_strings gt 1 then begin char_dim = self.obj_win -> GetTextDimensions(temp) spacing = char_dim[1]/n_old_strings loc[1, *] = loc[1, *] - transpose(indgen(n_lines)*spacing) endif if n_elements(data_scale) eq 2 then begin loc[0, *] = loc[0, *]+data_scale[0] ; Set up the location for the new text. loc[1, *] = loc[1, *]+data_scale[1] ; Set up the location for the new text. endif else begin loc[0, *] = former_position[0]+loc[0, *] ; Set up the location for the new text. loc[1, *] = former_position[1]+loc[1, *] ; Set up the location for the new text. endelse temp -> setProperty, strings=value, location=reform(loc), _extra=_extra endelse self.obj_win -> draw, self.view end ;************************************************************************* ; This is a narrow scope method for extracting the data from displayed plots. It can be utilized (at least ; was designed) for extracting data produced by legacy code for plotting, after PLOT command was substituted ; with DISPLAY. Now, this enables us to retain and reuse the data produced by that legacy code. ; Data are returned as a structure whos fields have same names as the plots the data were pulled out from, ; and field values are FLTARR(3, n) where (0, n) is X, (1, n) is Y and (2, n) is Z dimensions of the displayed data. ; Notice that the first field of the returned structure is VOID. function PARgrDisplay::get_data compile_opt IDL2, OBSOLETE self -> getProperty, plot_list=plot_list n_plots = n_elements(plot_list) if n_plots eq 0 then return, 0.0 output = create_struct('Void', 'Void') for i = 0, n_plots-1 do begin plot_list[i] -> getProperty, data=data, name=name if strlen(name) eq 0 then name = 'Plot '+strcompress(i, /rem) name = str_cleanup(name) output = create_struct(output, name, data) endfor return, output end ;************************************************************************* ; Initializes the object itself and creates widget system for displaying object data. function PARgrDisplay::init, parent=parent, group_leader=group_leader, ylog=ylog, xlog=xlog, $ font=font, charsize=charsize, window_xsize=window_xsize, window_ysize=window_ysize compile_opt IDL2, OBSOLETE ; Provide error handling. Catch, Err_code if (Err_code ne 0) then begin print,'PARgrDisplay::init error caught:' help, /last_message catch, /cancel return, 0 endif ; If Log keywords are not provided, init them as No. if not keyword_set(xlog) then xlog = 0b if not keyword_set(ylog) then ylog = 0b if n_elements(window_xsize) eq 0 then window_xsize = 600 if n_elements(window_ysize) eq 0 then window_ysize = 350 self.window_xsize = window_xsize self.window_ysize = window_ysize ; Make it easy to assign values to pointer fields. temp = n_tags({PARgrDisplay}) for i=0, temp-1 do if size(self.(i), /type) eq 10 then self.(i) = ptr_new(/allocate) a_child = 0b ; Assume we are creating independent window. ; Check if Parent was provided. If no, create top base. If yes and Parent is a base, use it. if n_elements(parent) ne 0 then $ ; Check if Parent is even provided... if widget_info(parent, /valid_id) then $ ; Parent is a valid widget... if widget_info(parent, /type) eq 0 then a_child = 1b ; and it is a base. Set a flag variable. if a_child then begin self.top_ID = widget_base(Parent, uvalue=self) endif else begin self.top_ID = widget_base(/kbrd_focus_ev, /tlb_size_ev, uvalue=self) widget_control, self.top_ID, tlb_set_title='Display window '+strtrim(self.top_ID, 2) endelse if n_elements(group_leader) ne 0 then $ ; Check if group_leader is even provided... if widget_info(group_leader, /valid_id) then $ ; group_leader is a valid widget... widget_control, self.top_ID, group_leader=group_leader ; Use that ID as group leader. ; Draw area is a child of self.top_ID regardless of whether or not parent was provided. draw_ID = widget_draw(self.top_ID, graphics=2, /button_event, xsize=window_xsize, $ ysize=window_ysize, event_pro='Display_event', retain=2, uvalue=self) ; Need to realize it all now, because we can only get value of realized widget_draw in object mode. widget_control, self.top_ID, /realize xmanager, 'display', self.top_ID, /No_block, cleanup='display_cleanup' ;======================== OBJECT INITIALIZATION START ============================ widget_control, draw_ID, get_value=Obj_Win self.obj_win = obj_win self.container = obj_new('IDL_Container') self.axes_list = obj_new('IDL_Container') self.title = obj_new('IDL_Container') self.roi_color = [255,0,0] ; Make X and Y ranges. yrange = (xrange = [0.0, 1.0]) ; Normalize data. n_xr = normalize(xrange) n_yr = normalize(yrange) ; Set ranges in the object. They would not be used normally, only if a user is clicking randomly. self.xrange = xrange self.yrange = yrange ; Need to create new Model, View etc. objects. model = obj_new('IDLgrModel') view = obj_new('IDLgrView', viewplane_rect=[-0.1, -0.1, 1.12, 1.16], location=[0.0, 0.0]) ; Create window-wide font, if requested. If not requested, default of Helvetica 12 pt will be used. if size(font, /type) eq 7 then $ if n_elements(charsize) ne 0 then begin charsize = float(charsize) default_font = obj_new('IDLgrFont', font, size=12.0*charsize) endif else default_font = obj_new('IDLgrFont', font, size=12.0) ; Create and initialize axes. x_axis = obj_new('IDLgrAxis', 0, ticklen=0.03, name='X_AXIS', location=[1000, 0, 0], /exact, log=xlog, _EXTRA=extra) y_axis = obj_new('IDLgrAxis', 1, ticklen=0.03, name='Y_AXIS', location=[0, 1000, 0], /exact, log=ylog, _EXTRA=extra) ; Create upper and right axes. x_axis_top = obj_new('IDLgrAxis', 0, ticklen=0.03, tickdir=1, loc=[1000, 1, 0], /notext, /exact, log=xlog, _EXTRA=extra) y_axis_top = obj_new('IDLgrAxis', 1, ticklen=0.03, tickdir=1, loc=[1, 1000, 0], /notext, /exact, log=ylog, _EXTRA=extra) ; Only place labels on the left and bottom axes. x_axis -> getProperty, ticktext=x_tick_labels y_axis -> getProperty, ticktext=y_tick_labels x_tick_labels -> setProperty, recompute=2, font=default_font y_tick_labels -> setProperty, recompute=2, font=default_font ; Create default axis titles. x_title = obj_new('IDLgrText', 'X title', font=default_font, recompute=2) y_title = obj_new('IDLgrText', 'Y title', font=default_font, recompute=2) x_axis -> setProperty, title=x_title, tickformat='(G0)' y_axis -> setProperty, title=y_title, tickformat='(G0)' ; Create plot title. title = obj_new('IDLgrText', font=default_font, recompute=2, location=[0.5, 1.02], align=0.5) ; Set axes ranges and remove extra zeros from tick labels if they are present. X_axis -> setProperty, range=xrange, xcoord_conv=n_xr Y_axis -> setProperty, range=yrange, ycoord_conv=n_yr X_axis_top -> setProperty, range=xrange, xcoord_conv=n_xr Y_axis_top -> setProperty, range=yrange, ycoord_conv=n_yr ; Add atomic objects to the model. model -> add, x_axis model -> add, y_axis model -> add, x_axis_top model -> add, y_axis_top model -> add, title ; Add model to its view and add view to the view. view -> add, model ; Store View in PARgrDisplay. self.view = view ; Need to store all newly created objects in CONTAINER so that they can be easily found or killed. second_layer = obj_new('IDLgrModel') ; This model will hold text objects and legend. view -> add, second_layer self.container -> add, model ; To locate with ease. Pos 0 self.container -> add, second_layer ; To locate with ease. Pos 0 if obj_valid(default_font) then self.container -> add, default_font ; To kill with ease. ; Create separate container for Axes. self.axes_list -> add, x_axis self.axes_list -> add, y_axis self.axes_list -> add, x_axis_top self.axes_list -> add, y_axis_top ; Create container for titles. self.title -> add, title ; To locate or kill with ease. Pos 0 self.title -> add, x_title ; To locate or kill with ease. Pos 1 self.title -> add, y_title ; To locate or kill with ease. Pos 2 ; Update the plot. Obj_win -> draw, self.view ;======================== OBJECT INITIALIZATION END ============================== ; Initialize container for plots. self.plot_list = obj_new('IDL_Container') self.symbol_list = obj_new('IDL_Container') self.trash = obj_new('IDL_Container') return, 1 end ;************************************************************************* ; Universal cleanup routine. Kills everything it finds, but will miss nested pointers or ORefs. pro PARgrDisplay::cleanup compile_opt IDL2, OBSOLETE ;Release all pointers and object references that are present in PARgrDisplay object. tags = n_tags({PARgrDisplay}) for i=0, tags-1 do begin if size(self.(i), /type) eq 10 then ptr_free, self.(i) if size(self.(i), /type) eq 11 then obj_destroy, self.(i) endfor end ;************************************************************************* ; PARgrDisplay definition procedure. pro PARgrDisplay__define compile_opt IDL2, OBSOLETE temp = fltarr(2) result = {PARgrDisplay, $ ; Name of class top_ID: 0L, $ ; Top base widget ID wtitle: '', $ ; Title of the window link: 0b, $ ; Flag indicating that the object is to be used for linking of ROIs. obj_win: obj_new(), $ ; Draw window reference view: obj_new(), $, ; View - for easy drawing. title: obj_new(), $ ; Container with titles for plot and axes container: obj_new(), $ ; Container for easy clean-up trash: obj_new(), $ ; Container object for disposable items. plot_list: obj_new(), $ ; List of all plots for easy access symbol_list: obj_new(), $ ; List of symbols for each plot for easy access axes_list: obj_new(), $ ; List of all axes for easy access ROI: obj_new(), $ ; Temporary storage for ROI or zooming rectangle. roi_color: bytarr(3), $ ; Color of the ROI. xrange: temp, $ ; X range vector yrange: temp, $ ; Y range vector norm_x: temp, $ ; XCOORD_CONV setting norm_y: temp, $ ; YCOORD_CONV setting mpress: intarr(2), $ ; Location of mouse press mouse_button: 0b, $ ; Mouse button type - 1 right, 2 middle, 4 left. window_xsize: intarr(2), $ ; Horizontal size of the DISPLAY window window_ysize: intarr(2), $ ; Vertical size of the DISPLAY window uvalue: ptr_new()} ; User value end ;************************************************************************* ; Procedure to allow calling Cleanup method on the PARgrDisplay by Xmanager. This is needed ; because an object method can is not allowed in a call to Xmanager. pro display_cleanup, top_ID Common PARgrDisplay_top_window, got_focus widget_control, top_ID, get_uval=Object ; Get object reference and destroy it. obj_destroy, Object got_focus = got_focus[where(obj_valid(got_focus)) > 0] ; Clean up the common block. end ;************************************************************************* ; Dummy procedure to isolate the user from the object syntax of PARgrDisplay. ; Tracking of independent windows is provided through common block. Update of common block ; occurs in the event procedure, when focus changes. Therefore, daughter windows provide no updates ; to common block, because they can not generate focus events. This is the desired behavior. pro display, x, y, ylog=ylog, xlog=xlog, over=over, psym=psym, printout=printout, charsize=charsize, $ parent=parent, group_leader=group_leader, oref=oref, $ ; These are for programmatic access to a Display window. copy=copy, save=save, restore_file=restore_file, _extra=_extra compile_opt IDL2, OBSOLETE Common PARgrDisplay_top_window, temp filename = size(restore_file, /type) if filename ne 0 then begin ; Restore a saved DISPLAY. call_procedure, 'IDLgrLegend__define' ; Create the right definition of IDLgrLegend if filename eq 7 then filename = restore_file else filename = dialog_pickfile(filter='*.dis') restore, filename self -> gui return ; No other action is performed, because you need to see what you have first. endif if n_elements(temp) ne 0 then $ if obj_valid(temp[0]) and (not obj_valid(oref)) then oref = temp[0] ; We can afford to drop the object reference here, because it is still stored in the widget's Uvalue. ; Object will be deleted when the widget is closed. ; Provide error handling. Catch, Err_code if (Err_code ne 0) then begin print,'DISPLAY error caught:' help, /last_message catch, /cancel return endif ; Create new instance if required, or used existing one to add items. Generate error on a mistake. if not keyword_set(over) then oref = obj_new('PARgrDisplay', ylog=ylog, xlog=xlog, charsize=charsize, $ parent=parent, group_leader=group_leader) if not obj_valid(oref) then message, 'No window exists to overplot.' ; Set all necessary properties, update the plot. if n_params() ne 0 then oref -> oplot, x, y, psym=psym, _extra=_extra ; The following line serves in several ways. First, it allows to bypass listing keywords for setProperty. ; Second, it will pass any extra keywords to the named plot object, if name is provided. oref -> setProperty, psym=psym, _extra=_extra, /draw if keyword_set(copy) then oref -> copy, _extra=_extra ; Lastly, print it out if requested. if keyword_set(printout) then oref -> printout if keyword_set(save) then oref -> save, _extra=_extra end