function varargout = tabpanelcopyfcn(fcn, varargin)
% TABPANELCOPYFCN Helper function for tabpanelcopy
% Creates replicas of a panel created using GUIDE that works like a tab
% panel.
% Handles of the reference controls in the handles struct are updated every
% time you change the active tab, so you can access them in the normal way.
%
% Steps:
% - 1. Create a reference panel with controls with GUIDE.
% - 2. Initialize tab panel passing the handle of the panel.
% - 3. Add a tab when you need it.
%
%
% tabpanelcopyfcn ('initialize', panel);
% - Create the structure of handles.
% - Creates the controls that hide part of the controls.
%
% [handles,tab] = tabpanelcopyfcn ('add_tab', panel, 'title', handles);
% - Replicates reference handles.
% - Adds a new button as a tab label and returns it (only needed if you plan to
% call 'change_tab' programmatically).
% - Since handles changes, remember to refresh handles structure on the caller!
%
% handles = tabpanelcopyfcn ('change_tab', hObject, eventdata, panel, handles);
% - "Private" function.
% - Hides visible childs of panel, and shows the ones of next active tab.
% - To change to a tab programmatically, just execute the callback on the
% tab handler returned by 'add_tab' fcn.
%
% tabpanelcopyfcn ('close_tab', hObject, eventdata, panel);
% - "Private" function.
% - Removes the active tab.
%
%
% Author: Arturo Serrano (arsercar@upvnet.upv.es)
% Created: Jun 04, 2009
% Updated: Jun 25, 2009
varargout = cell (1, nargout);
switch fcn
case 'initialize'
initialize(varargin{:});
case 'add_tab'
[varargout{:}] = add_tab(varargin{:});
case 'change_tab'
[varargout{:}] = change_tab(varargin{:});
case 'close_tab'
close_tab(varargin{:});
otherwise
error('Unrecognized command option. See HELP TABPANELCOPYFCN');
end
if nargout == 0
clear varargout;
end
%--------------------------------------------------------------------------
function initialize (panel)
% constants (modify if u want)
BORDERTYPE = 'beveledout'; % border of the tabpanel
LINE_OFFSET = [1 -1 -1 1]; % auxiliar control position
PATCH_OFFSET = [1 -16 -2 15]; % auxiliar control position
TABLIST_WIDTH = 19; % menu tablist
CLOSETAB_OFFSET = [3 1 -5 -4]; % button closetab
TAB_OFFSET = 10; % separation between tabs
TAB_WIDTH = 100; % tab width
TAB_HEIGHT = 22; % tab height (default)
LABEL_OFFSET = [-1 -1 +2 +4]; % label offset when is active
% we are gonna need these later
setappdata (panel, 'tablist_width', TABLIST_WIDTH);
setappdata (panel, 'tab_offset', TAB_OFFSET);
setappdata (panel, 'tab_width', TAB_WIDTH);
setappdata (panel, 'tab_height', TAB_HEIGHT);
setappdata (panel, 'label_offset', LABEL_OFFSET);
% position of panel
set (panel, 'title', [], 'bordertype', BORDERTYPE, 'visible', 'off');
% creates controls to pretty the thing
pos = get_position_pixels (panel);
% tab labels (buttons actually) are showed in front of the panel.
% a line makes the border of the panel.
% a patch hides the bottom of the label.
pos(2) = pos(2) + pos(4); % initial y of line and patch
pos(4) = 0; % initial height of line and patch
% line makes a mistake in a pixel near the end of the line. not very
% annoying, it could be solved with a white-backgrounded text
line = uicontrol ('style', 'edit', 'tag', 'border', 'units', 'normalized');
set_position_pixels (line, pos + LINE_OFFSET)
patch = uicontrol ('style', 'text', 'tag', 'patch', 'units', 'normalized');
set_position_pixels (patch, pos + PATCH_OFFSET);
% tab list menu
callback = @(hObject,eventdata) ...
change_tab (hObject, eventdata, panel, guidata (hObject));
tabList = uicontrol ('style', 'popupmenu', 'tag', 'tablist', ...
'enable', 'off', 'string', {''}, 'callback', callback, ...
'units', 'normalized');
tablistpos = get_position_pixels (tabList);
tablistpos(1) = pos(1) + pos(3) - TABLIST_WIDTH;
tablistpos(2) = pos(2);
tablistpos(3) = TABLIST_WIDTH;
set_position_pixels (tabList, tablistpos);
% close tab button (above an auxiliar popupmenu to mimic tabList look)
callback = @(hObject,eventdata) ...
close_tab (hObject, eventdata, panel, guidata (hObject));
closeTabAux = uicontrol ('style', 'popupmenu', 'tag', 'closetabaux', ...
'enable', 'off', 'string', {''}, ...
'units', 'normalized');
closetabpos = get_position_pixels (closeTabAux);
closetabpos(1) = pos(1) + pos(3) - TABLIST_WIDTH - TABLIST_WIDTH - 2;
closetabpos(2) = pos(2);
closetabpos(3) = TABLIST_WIDTH;
set_position_pixels (closeTabAux, closetabpos);
closeTab = uicontrol ('style', 'pushbutton', 'tag', 'closetab', ...
'string', 'x', 'fontweight', 'bold', ...
'enable', 'off', 'callback', callback, ...
'units', 'normalized');
set_position_pixels (closeTab, closetabpos + CLOSETAB_OFFSET);
% save data in tabs struct
setappdata (panel, 'tabs', struct ('label', {}, 'panel', {}, ...
'handles', {}, 'tags', {}));
setappdata (panel, 'active', 0);
setappdata (panel, 'tablist', tabList);
setappdata (panel, 'closetab', closeTab);
% and auxiliar controls
setappdata (panel, 'line', line);
setappdata (panel, 'patch', patch);
function [handles,new_tab] = add_tab (panel, title, handles)
% get the number of tabs, and create a "label"
tabs = getappdata (panel, 'tabs');
callback = @(hObject,eventdata) ...
change_tab (hObject, eventdata, panel, guidata (hObject));
new_tab = uicontrol ('style', 'pushbutton', 'tag', 'tab', ...
'enable', 'inactive', 'string', title, ...
'buttondownfcn', callback, ...
'units', 'normalized');
set_label_position (new_tab, numel(tabs)+1, panel);
% replicate reference panel
new_panel = copyobj (panel, get (panel, 'parent'));
set (new_panel, 'visible', 'on');
% save tags of children
childs = findobj (get (new_panel, 'children'));
tags = get (childs, 'tag');
if numel (tags) == 1, tags = {tags}; end % tags must be a cell
% update tab list
tabList = getappdata (panel, 'tablist');
closeTab = getappdata (panel, 'closetab');
if numel (tabs) == 0
contents = {title};
% enable el tabList y closetab
set (tabList, 'enable', 'on');
set (closeTab, 'enable', 'on');
else
contents = get (tabList, 'string');
contents{end+1} = title;
end
set (tabList, 'string', contents);
% save them in the tab structure
tabs(end+1).label = new_tab;
tabs(end).panel = new_panel;
tabs(end).handles = childs;
tabs(end).tags = tags;
setappdata (panel, 'tabs', tabs);
handles = change_tab (new_tab, [], panel, handles);
function varargout = change_tab (hObject, eventdata, panel, handles) %#ok<INUSL>
% callback from tab labels or tab list: new_tab can be a button or a popmenu
% offset of labels when they are active
label_offset = getappdata (panel, 'label_offset');
% tabs
tabs = getappdata (panel, 'tabs');
% set current tab off
active = getappdata (panel, 'active');
if active > 0
% change position of label (and color)
pos = get_position_pixels (tabs(active).label);
set_position_pixels (tabs(active).label, pos - label_offset);
% set (tabs(active).label, 'background', 'white');
% moving the active panel through the uistack doesn't seems to work...
% so we must hide it
set (tabs(active).panel, 'visible', 'off');
end
% extract active tab depending on wether hObject is a button or a menu
if strcmp (get (hObject, 'style'), 'popupmenu')
active = get (hObject, 'value');
new_tab = tabs(active).label;
elseif strcmp (get (hObject, 'style'), 'pushbutton')
active = find ([tabs.label] == hObject);
new_tab = hObject;
% update value of tablist
set (getappdata (panel, 'tablist'), 'value', active);
end
% set new tab on and show it
if new_tab
set_position_pixels (new_tab, get_position_pixels (new_tab) + label_offset);
set (tabs(active).panel, 'visible', 'on');
% set (new_tab, 'background', get(0,'defaultUicontrolBackgroundColor'));
end
% update the graphic patches
uistack ([getappdata(panel,'patch'), ...
new_tab, getappdata(panel,'line')], ...
'top'); % order matters: first go top
% change caller handles by children of this panel
childs = tabs(active).handles;
tags = tabs(active).tags;
for itag = 1:numel (tags)
handles.(tags{itag}) = childs(itag);
end
% update guidata
guidata (panel, handles);
% save data
setappdata (panel, 'active', active);
if nargout == 1
varargout{1} = handles;
end
function close_tab (hObject, eventdata, panel, handles) %#ok<INUSL>
tabs = getappdata (panel, 'tabs');
% get the active tab from tablist
active = get (getappdata (panel, 'tablist'), 'value');
% delete handles of the active tab
delete (tabs(active).label);
delete (tabs(active).panel);
% delete (tabs(active).handles);
% delete from tabs struct and update
tabs(active) = [];
setappdata (panel, 'tabs', tabs);
setappdata (panel, 'active', 0); % dont disable any tab in change_tab
% update tab list
tabList = getappdata (panel, 'tablist');
contents = get (tabList, 'string');
contents(active) = [];
if isempty (contents), contents = {''}; end % cant be empty
set (tabList, 'string', contents);
% new active tab (last if was last, next if not)
active = min (active, numel (tabs));
% change to new tab
if active > 0
% reorder tabs from the active to last
for idx = active:numel (tabs)
set_label_position (tabs(idx).label, idx, panel);
end
change_tab (tabs(active).label, eventdata, panel, handles);
else
% disable tablist and closetab
set (tabList, 'enable', 'off');
set (getappdata (panel, 'closetab'), 'enable', 'off');
end
%-------------------------------------------------------------------------------
function set_label_position (label, idx, panel)
tab_offset = getappdata (panel, 'tab_offset');
tab_width = getappdata (panel, 'tab_width');
tab_height = getappdata (panel, 'tab_height');
% position of the labels
pos = get_position_pixels (panel);
tab_x = pos(1) + (tab_width+tab_offset)*(idx-1) + tab_offset;
tab_y = pos(2) + pos(4) - 5;
set_position_pixels (label, [tab_x, tab_y, tab_width, tab_height]);
% change visible property
tablist_width = getappdata (panel, 'tablist_width');
% tab ends beyond closetab position (a little more)
if tab_x+tab_width < pos(1)+pos(3)-3*tablist_width
set (label, 'visible', 'on')
else
set (label, 'visible', 'off');
end
%-------------------------------------------------------------------------------
function set_position_pixels(hObject, position)
units = get (hObject, 'units');
set (hObject, 'units', 'pixels');
set (hObject, 'position', position)
set( hObject, 'units', units);
function position = get_position_pixels (hObject)
units = get (hObject, 'units');
set (hObject, 'units', 'pixels');
position = get (hObject, 'position');
set (hObject, 'units', units);