source: MML/trunk/machine/SOLEIL/common/toolbox/GUILayout/+uiextras/Container.m @ 4

Last change on this file since 4 was 4, checked in by zhangj, 11 years ago

Initial import--MML version from SOLEIL@2013

File size: 21.9 KB
Line 
1classdef Container < hgsetget
2    %Container  Container base class
3    %
4    %   c = uiextras.Container() creates a new container object. Container
5    %   is an abstract class and can only be constructed as the first
6    %   actual of a descendent class.
7    %
8    %   c = uiextras.Container(param,value,...) creates a new container
9    %   object and sets one or more property values.
10    %
11    %   See also: uiextras.Box
12    %             uiextras.ButtonBox
13    %             uiextras.CardPanel
14    %             uiextras.Grid
15   
16    %   Copyright 2009-2010 The MathWorks, Inc.
17    %   1.1
18    %   2012/05/08 08:02:58
19   
20    properties
21        DeleteFcn       % function to call when the layout is being deleted [function handle]
22    end % Public properties
23   
24    properties( Dependent, Transient )
25        BackgroundColor % background color [r g b]
26        BeingDeleted    % is the object in the process of being deleted [on|off]
27        Children        % list of the children of the layout [handle array]
28        Enable          % allow interaction with the contents of this layout [on|off]
29        Parent          % handle of the parent container or figure [handle]
30        Position        % position [left bottom width height]
31        Tag             % tag [string]
32        Type            % the object type (class) [string]
33        Units           % position units [inches|centimeters|normalized|points|pixels|characters]
34        Visible         % is the layout visible on-screen [on|off]
35    end % dependent properties
36   
37    properties( Access = protected, Hidden, Transient )
38        Listeners = cell( 0, 1 ) % array of listeners
39    end % protected properties
40   
41    properties( SetAccess = private, GetAccess = protected, Hidden, Transient )
42        UIContainer % associated uicontainer
43    end % read-only protected properties
44   
45    properties( Access = private, Hidden, Transient )
46        Children_ = zeros( 0, 1 )     % private copy of the children list
47        ChildListeners = cell( 0, 2 ) % listeners for changes to children
48        Enable_ = 'on'                % private copy of the enabled state
49        CurrentSize_ = [0 0]          % private copy of the size
50    end % private properties
51   
52    methods
53       
54        function obj = Container( varargin )
55            %Container  Container base class constructor
56            %
57            %   obj = Container(param,value,...) creates a new Container
58            %   object using the (optional) property values specified. This
59            %   may only be called by child classes.
60           
61            % Check that we're using the right graphics version
62            if isHGUsingMATLABClasses()
63                error( 'GUILayout:WrongHGVersion', 'Trying to run using double-handle MATLAB graphics against the new graphics system. Please re-install.' );
64            end
65           
66            % Find if parent has been supplied
67            parent = uiextras.findArg( 'Parent', varargin{:} );
68            if isempty( parent )
69                parent = gcf();
70            end
71            units = uiextras.findArg( 'Units', varargin{:} );
72            if isempty( units )
73                units = 'Normalized';
74            end
75           
76            % Create container
77            args = {
78                'Parent', parent, ...
79                'Units', units, ...
80                'BorderType', 'none'
81                };
82            obj.UIContainer = uipanel( args{:} );
83                       
84            % Set the background color
85            obj.setPropertyFromDefault( 'BackgroundColor' );
86           
87            % Tag it!
88            set( obj.UIContainer, 'Tag', strrep( class( obj ), '.', ':' ) );
89           
90            % Create listeners to resizing of container
91            containerObj = handle( obj.UIContainer );
92            obj.Listeners{end+1,1} = handle.listener( containerObj, findprop( containerObj, 'PixelBounds' ), 'PropertyPostSet', @obj.onResized );
93           
94            % Create listeners to addition of container children
95            obj.Listeners{end+1,1} = handle.listener( containerObj, 'ObjectChildAdded', @obj.onChildAddedEvent );
96           
97            % Watch out for the graphics being destroyed
98            obj.Listeners{end+1,1} = handle.listener( containerObj, 'ObjectBeingDestroyed', @obj.onContainerBeingDestroyed );
99           
100            % Store Container in container
101            setappdata( obj.UIContainer, 'Container', obj );
102
103        end % constructor
104       
105        function container = double( obj )
106            %double  Convert a container to an HG double handle.
107            %
108            %  D = double(C) converts a container C to an HG handle D.
109            container = obj.UIContainer;
110        end % double
111       
112        function pos = getpixelposition( obj )
113            %getpixelposition  get the absolute pixel position
114            %
115            %   POS = GETPIXELPOSITION(C) gets the absolute position of the container C
116            %   within its parent window. The returned position is in pixels.
117            pos = getpixelposition( obj.UIContainer );
118        end % getpixelposition
119       
120        function tf = isprop( obj, name )
121            %isprop  does this object have the specified property
122            %
123            %   TF = ISPROP(C,NAME) checks whether the object C has a
124            %   property named NAME. The result, TF, is true if the
125            %   property exists, false otherwise.
126            tf = ismember( name, properties( obj ) );
127           
128        end % isprop
129       
130        function p = ancestor(obj,varargin)
131            %ancestor  Get object ancestor
132            %
133            %   P = ancestor(H,TYPE) returns the handle of the closest ancestor of h
134            %   that matches one of the types in TYPE, or empty if there is no matching
135            %   ancestor.  TYPE may be a single string (single type) or cell array of
136            %   strings (types). If H is a vector of handles then P is a cell array the
137            %   same length as H and P{n} is the ancestor of H(n). If H is one of the
138            %   specified types then ancestor returns H.
139            %
140            %   P = ANCESTOR(H,TYPE,'TOPLEVEL') finds the highest level ancestor of one
141            %   of the types in TYPE
142            %
143            %   If H is not an Handle Graphics object, ANCESTOR returns empty.
144            p = ancestor( obj.UIContainer, varargin{:} );
145        end %ancestor
146       
147        function delete( obj )
148            %delete  destroy this layout
149            %
150            % If the user destroys the object, we *must* also remove any
151            % graphics
152            if ~isempty( obj.DeleteFcn )
153                uiextras.callCallback( obj.DeleteFcn, obj, [] );
154            end
155            if ishandle( obj.UIContainer ) ...
156                    && ~strcmpi( get( obj.UIContainer, 'BeingDeleted' ), 'on' )
157                delete( obj.UIContainer );
158            end
159        end % delete
160       
161    end % public methods
162   
163    methods
164       
165        function set.Position( obj, value )
166            set( obj.UIContainer, 'Position', value );
167        end % set.Position
168       
169        function value = get.Position( obj )
170            value = get( obj.UIContainer, 'Position' );
171        end % get.Position
172       
173        function set.Children( obj, value )
174            % Check
175            oldChildren = obj.Children_;
176            newChildren = value;
177            [tf, loc] = ismember( oldChildren, newChildren );
178            if ~isequal( size( oldChildren ), size( newChildren ) ) || any( ~tf )
179                error( 'GUILayout:Container:InvalidPropertyValue', ...
180                    'Property ''Children'' may only be set to a permutation of itself.' )
181            end
182           
183            % Set
184            obj.Children_ = newChildren;
185           
186            % Reorder ChildListeners
187            obj.ChildListeners(loc,:) = obj.ChildListeners;
188           
189            % Redraw
190            obj.redraw();
191        end % set.Children
192       
193        function value = get.Children( obj )
194            value = obj.Children_;
195        end % get.Children
196       
197        function set.Enable( obj, value )
198            % Check
199            if ~ischar( value ) || ~ismember( lower( value ), {'on','off'} )
200                error( 'GUILayout:Container:InvalidPropertyValue', ...
201                    'Property ''Enable'' may only be set to ''on'' or ''off''.' )
202            end
203            % Apply
204            value = lower( value );
205            % If we want to switch on but our parent is off, just store
206            % in the app data.
207            if strcmp( value, 'on' )
208                if isappdata( obj.Parent, 'Container' )
209                    parentObj = getappdata( obj.Parent, 'Container' );
210                    if strcmpi( parentObj.Enable, 'off' )
211                        setappdata( obj.UIContainer, 'OldEnableState', value );
212                        value = 'off';
213                    end
214                end
215            end
216            obj.Enable_ = value;
217           
218            % Apply to children
219            ch = obj.Children_;
220            for ii=1:numel( ch )
221                obj.helpSetChildEnable( ch(ii), obj.Enable_ );
222            end
223           
224            % Do the work
225            obj.onEnable( obj, value );
226        end % set.Enable
227       
228        function value = get.Enable( obj )
229            value = obj.Enable_;
230        end % get.Enable
231       
232        function set.Units( obj, value )
233            set( obj.UIContainer, 'Units', value );
234        end % set.Units
235       
236        function value = get.Units( obj )
237            value = get( obj.UIContainer, 'Units' );
238        end % get.Units
239       
240        function set.Parent( obj, value )
241            set( obj.UIContainer, 'Parent', double( value ) );
242        end % set.Parent
243       
244        function value = get.Parent( obj )
245            value = get( obj.UIContainer, 'Parent' );
246        end % get.Parent
247       
248        function set.Tag( obj, value )
249            set( obj.UIContainer, 'Tag', value );
250        end % set.Tag
251       
252        function value = get.Tag( obj )
253            value = get( obj.UIContainer, 'Tag' );
254        end % get.Tag
255       
256        function value = get.Type( obj )
257            value = class( obj );
258        end % get.Type
259       
260        function value = get.BeingDeleted( obj )
261            value = get( obj.UIContainer, 'BeingDeleted' );
262        end % get.BeingDeleted
263       
264        function set.Visible( obj, value )
265            set( obj.UIContainer, 'Visible', value );
266        end % set.Visible
267       
268        function value = get.Visible( obj )
269            value = get( obj.UIContainer, 'Visible' );
270        end % get.Visible
271       
272        function set.BackgroundColor( obj, value )
273            set( obj.UIContainer, 'BackgroundColor', value );
274            obj.onBackgroundColorChanged( obj, value );
275        end % set.BackgroundColor
276       
277        function value = get.BackgroundColor( obj )
278            value = get( obj.UIContainer, 'BackgroundColor' );
279        end % get.BackgroundColor
280       
281    end % accessor methods
282   
283    methods( Access = protected )
284       
285        function onResized( obj, source, eventData ) %#ok<INUSD>
286            %onResized  Callback that fires when a container is resized.
287            newSize = getpixelposition( obj );
288            newSize = newSize([3,4]);
289            if any(newSize ~= obj.CurrentSize_)
290                % Size has changed, so must redraw
291                obj.CurrentSize_ = newSize;
292                obj.redraw();
293            end
294        end % onResized
295       
296        function onContainerBeingDestroyed( obj, source, eventData ) %#ok<INUSD>
297            %onContainerBeingDestroyed  Callback that fires when the container dies
298            delete( obj );
299        end % onContainerBeingDestroyed
300       
301        function onChildAdded( obj, source, eventData ) %#ok<INUSD>
302            %onChildAdded  Callback that fires when a child is added to a container.
303            obj.redraw();
304        end % onChildAdded
305       
306        function onChildRemoved( obj, source, eventData ) %#ok<INUSD>
307            %onChildRemoved  Callback that fires when a container child is destroyed or reparented.
308            obj.redraw();
309        end % onChildRemoved
310       
311        function onBackgroundColorChanged( obj, source, eventData ) %#ok<INUSD,MANU>
312            %onBackgroundColorChanged  Callback that fires when the container background color is changed
313        end % onChildRemoved
314       
315        function onEnable( obj, source, eventData ) %#ok<INUSD,MANU>
316            %onEnable  Callback that fires when the enable state is changed
317        end % onChildRemoved
318       
319        function c = getValidChildren( obj )
320            %getValidChildren  Return a list of only those children not being deleted
321            c = obj.Children;
322            c( strcmpi( get( c, 'BeingDeleted' ), 'on' ) ) = [];
323        end % getValidChildren
324       
325        function repositionChild( obj, child, position )
326            %repositionChild  adjust the position and visibility of a child
327            if position(3)<=0 || position(4)<=0
328                % Not enough space, so move offscreen instead
329                set( child, 'Position', [-100 -100 10 10] );
330            else
331                % There's space, so make sure visibility is on
332                % First determine whether to use "Position" or "OuterPosition"
333                if isprop( child, 'ActivePositionProperty' )
334                    propname = get( child, 'ActivePositionProperty' );
335                else
336                    propname = 'Position';
337                end
338               
339                % Now set the position in pixels, changing the units first if
340                % necessary
341                oldunits = get( child, 'Units' );
342                if strcmpi( oldunits, 'Pixels' )
343                    set( child, propname, position );
344                else
345                    % Other units, so switch to pixels before setting
346                    set( child, 'Units', 'pixels' );
347                    set( child, propname, position );
348                    set( child, 'Units', oldunits );
349                end
350            end
351        end % repositionChild
352       
353        function setPropertyFromDefault( obj, propName )
354            %getPropertyDefault  Retrieve a default property value. If the
355            %value is not found in the parent or any of its ancestors the
356            %supplied defValue is used.
357            error( nargchk( 2, 2, nargin ) );
358           
359            parent = get( obj.UIContainer, 'Parent' );
360            myClass = class(obj);
361            if strncmp( myClass, 'uiextras.', 9 )
362                myClass = myClass(10:end);
363            end
364            defPropName = ['Default',myClass,propName];
365           
366            % Getting the default will fail if the default does not exist
367            % of has an invalid value. In that case we leave the current
368            % value as it is.
369            try
370                obj.(propName) = uiextras.get( parent, defPropName );
371            catch err %#ok<NASGU>
372                % Failed, so leave it alone
373            end
374        end % setPropertyFromDefault
375       
376        function helpSetChildEnable( ~, child, state )
377            % Set the enabled state of one child widget
378            if strcmpi( get( child, 'Type' ), 'uipanel' )
379                % Might be another layout
380                if isappdata( child, 'Container' )
381                    child = getappdata( child, 'Container' );
382                else
383                    % Can't enable a panel
384                    child = [];
385                end
386            elseif isprop( child, 'Enable' )
387                % It supports enabling directly
388            else
389                % Doesn't support enabling
390                child = [];
391            end
392           
393            if ~isempty( child )
394           
395            % We will use a piece of app data
396            % to track the original state to ensure we don't
397            % re-enable something that shouldn't be.
398            if strcmpi( state, 'On' )
399                if isappdata( child, 'OldEnableState' )
400                    set( child, 'Enable', getappdata( child, 'OldEnableState' ) );
401                    rmappdata( child, 'OldEnableState' );
402                else
403                    set( child, 'Enable', 'on' );
404                end
405            else
406                if ~isappdata( child, 'OldEnableState' )
407                    setappdata( child, 'OldEnableState', get( child, 'Enable' ) );
408                end
409                set( child, 'Enable', 'off' );
410            end
411            end
412        end % helpSetChildEnable
413       
414    end % protected methods
415   
416    methods( Abstract = true, Access = protected )
417       
418        redraw( obj )
419       
420    end % abstract methods
421   
422    methods( Access = private, Sealed = true )
423       
424        function onChildAddedEvent( obj, source, eventData ) %#ok<INUSL>
425            %onChildAddedEvent  Callback that fires when a child is added to a container.
426           
427            % Find child in Children
428            child = eventData.Child;
429            if ismember( child, obj.Children_ )
430                return % not *really* being added
431            end
432           
433            % Only hook up internally if not a "hidden" child.
434            if ~isprop( child, 'HandleVisibility' ) ...
435                    || strcmpi( get( child, 'HandleVisibility' ), 'off' )
436                return;
437            end
438           
439            % We don't want to do anything to the panel title
440            if isappdata( obj.UIContainer, 'PanelTitleCreate' ) ...
441                    && getappdata( obj.UIContainer, 'PanelTitleCreate' )
442                % This child is the panel label. Set its visibility off so
443                % we don't see it again.
444                set( child, 'HandleVisibility', 'off' );
445                return;
446            end
447           
448            % We also need to ignore legends as they are positioned by
449            % their associated axes.
450            if isa( child, 'axes' ) && strcmpi( get( child, 'Tag' ), 'legend' )
451                return;
452            end
453           
454            % Add element to Children
455            obj.Children_(end+1,:) = child;
456           
457            % Add elements to ChildListeners. A bug in R2009a and
458            % earlier means we have to be careful about this
459            if isBeforeR2009b()
460                obj.ChildListeners(end+1,:) = ...
461                    {handle.listener( child, 'ObjectBeingDestroyed', {@helpDeleteChild,obj} ), ...
462                    handle.listener( child, 'ObjectParentChanged', {@helpReparentChild,obj} )};
463            else
464                obj.ChildListeners(end+1,:) = ...
465                    {handle.listener( child, 'ObjectBeingDestroyed', @obj.onChildBeingDestroyedEvent ), ...
466                    handle.listener( child, 'ObjectParentChanged', @obj.onChildParentChangedEvent )};
467            end
468           
469            % We are taking over management of position and will do it
470            % in either pixel or normalized units.
471            units = lower( get( child, 'Units' ) );
472            if ~ismember( units, {'pixels' ,'normalized'} )
473                set( child, 'Units', 'Pixels' );
474            end
475           
476            % If we are disabled, make sure the children are too
477            if strcmpi( obj.Enable_, 'off' )
478                helpSetChildEnable( obj, child, obj.Enable_ );
479            end
480           
481            % Call onChildAdded
482            eventData = uiextras.ChildEvent( child, numel( obj.Children_ ) );
483           
484            obj.onChildAdded( obj, eventData );
485        end % onChildAddedEvent
486       
487        function onChildBeingDestroyedEvent( obj, source, eventData ) %#ok<INUSD>
488            %onChildBeingDestroyedEvent  Callback that fires when a container child is destroyed.
489           
490            % Find child in Children
491            [dummy, loc] = ismember( source, obj.Children_ ); %#ok<ASGLU>
492           
493            % Remove element from Children
494            obj.Children_(loc,:) = [];
495           
496            % Remove elements from ChildListeners
497            obj.ChildListeners(loc,:) = [];
498           
499            % If we are in our death throes, don't start calling callbacks
500            if ishandle( obj.UIContainer ) && ~strcmpi( get( obj.UIContainer, 'BeingDeleted' ), 'ON' )
501                % Call onChildRemoved
502                eventData = uiextras.ChildEvent( source, loc );
503                obj.onChildRemoved( obj, eventData );
504            end
505           
506        end % onChildBeingDestroyedEvent
507       
508        function onChildParentChangedEvent( obj, source, eventData )
509            %onChildParentChangedEvent  Callback that fires when a container child is reparented.
510           
511            if isempty( eventData.NewParent ) ...
512                    || eventData.NewParent == obj.UIContainer
513                return % not being reparented *away*
514            end
515           
516            % Find child in Children
517            [dummy, loc] = ismember( source, obj.Children_ ); %#ok<ASGLU>
518           
519            % Remove element from Children
520            obj.Children_(loc,:) = [];
521           
522            % Remove elements from ChildListeners
523            obj.ChildListeners(loc,:) = [];
524           
525            % Call onChildRemoved
526            eventData = uiextras.ChildEvent( source, loc );
527            obj.onChildRemoved( obj, eventData );
528           
529        end % onChildParentChangedEvent
530       
531    end % private sealed methods
532   
533end % classdef
534
535% -------------------------------------------------------------------------
536
537% Helper functions to work around a bug in R2009a and earlier
538
539function ok = isBeforeR2009b()
540persistent matlabVersionDate;
541if isempty( matlabVersionDate )
542    v = ver( 'MATLAB' );
543    matlabVersionDate = datenum( v.Date );
544%     uiwait( msgbox( sprintf( 'Got MATLAB version date: %s', v.Date ) ) )
545end
546ok = ( matlabVersionDate <= datenum( '15-Jan-2009', 'dd-mmm-yyyy' ) );
547end
548
549
550function helpDeleteChild( src, evt, obj )
551obj.onChildBeingDestroyedEvent( src, evt );
552end % helpDeleteChild
553
554function helpReparentChild( src, evt, obj )
555obj.onChildParentChangedEvent( src, evt );
556end % helpReparentChild
557
558
559       
Note: See TracBrowser for help on using the repository browser.