function e = querycallbacks(callaction)
%QUERYCALLBACKS Switchyard of visual query builder callbacks.
%   E = QUERYCALLBACKS(CALLACTION) is the switchyard of visual query builder callbacks.
%   The switch expression CALLACTION can be passed to the routine or it can be determined
%   by the routine based on the callback object.   E is the error flag output used by the
%   'errors' switch expression only.

%   Author(s): C.F.Garvin, 05-07-98
%   Copyright (c) 1984-2000 The MathWorks, Inc.
%   $Revision: 1.29 $   $Date: 2000/08/16 19:17:48 $
    
set(findobj('Type','figure'),'Pointer','watch')

%GUI spacing parameters
dfp = get(0,'Defaultfigureposition');
mfp = [560 420];
bspc = mean([5/mfp(2)*dfp(4) 5/mfp(1)*dfp(3)]);
bhgt = 20/mfp(2) * dfp(4);
bwid = 80/mfp(1) * dfp(3);

%Get Tag or String of current callback object
t = get(gcbo,'Type');
if ~any(strcmp(t,{'uimenu','figure'})) 
  style = get(gcbo,'Style');
else
  style = 'dummystring';
end

if nargin == 1
  %Callaction has been passed as input argument
else
  callaction = get(gcbo,'Tag');
end

%Get current SQL statement
qobj = findobj(gcf,'Tag','query');
querystr = get(qobj,'String');

%Get various uicontrol objects for use in the switchyard
dobj = findobj(gcf,'Tag','querydata');     %Query data listbox
fobj = findobj(gcf,'Tag','fields');        %Fields listbox
gobj = findobj(gcf,'Tag','groupbyclausedata');   
hobj = findobj(gcf,'Tag','havingclausedata');
oobj = findobj(gcf,'Tag','orderbyclausedata');
robj = findobj(gcf,'Tag','AoD');           %All/Distinct radio buttons
wobj = findobj(gcf,'Tag','whereclausedata'); 
sobj = findobj(gcf,'Tag','sources');       %Sources listbox
sstr = get(sobj,'String');
sval = get(sobj,'Value');
if ~isempty(sval)
  sval = sval(1);
  set(sobj,'Value',sval)                     %Do not allow multiselect
  sourcestr = sstr{sval};                    %Datasource string
end  
tobj = findobj(gcf,'Tag','tables');        %Tables listbox
vobj = findobj(gcf,'Tag','wkvariable');    %Workspace variable string

switch callaction
  case 'AoD'         %Add ALL or DISTINCT keyword to query
    
    %Update values of radiobuttons and rebuild query string       
    set(robj,'Value',0);
    set(gcbo,'Value',1);
    if ~isempty(querystr)
      buildquery;
    end
    
  case 'Cancel'    %Cancel callback
    
    close
    
  case 'cleanupdialog'   %Run GUI cleanup routine
    
    cleanupdialog;
    
  case 'errors'      %Need error checking if possible to provide bad query info
    
    %Get data needed to look for possible problems
    fdat = get(fobj,{'String','Value'});
    gdat = get(gobj,'Userdata');
    hdat = get(hobj,'Userdata');
    odat = get(oobj,'Userdata');
    e = 0;
    
    %If no group by clauses, there should be no problems
    if isempty(gdat)
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end
    
    %Need connection  and dmd to determine supported GROUP BY properties
    conn = loginconnect(sourcestr);
    if strcmp(class(conn.Handle),'double')
      set(findobj('Type','figure'),'Pointer','arrow')
      return    %Invalid connection, stop processing
    end
    d = dmd(conn);

    %GROUP BY clause must reference only fields chosen for select
    gfld = gdat{1};
    ffld = {fdat{1}{fdat{2}}};
    noerror = 1;
    
    %Determine supported Group By properties
    if supports(d,'GroupByUnrelated')
    elseif supports(d,'GroupByBeyondSelect')
      for i = 1:length(ffld)
        if ~strcmp(ffld(i),gfld)
          noerror = 0;
          break
        end
      end
    end
    close(conn)
    
    if ~noerror  %Found field in GROUP BY clause no selected in statement
      errordlg('GROUP BY clause must reference all fields found in SELECT statement.');
      set(findobj('Type','figure'),'Pointer','arrow')
      e = 1;
      return
    end
    
    %Get GROUP BY and HAVING fields clashes
    if ~isempty(hdat)
      hfld = hdat{1};
      for i = 1:length(hfld)
        tmpfld = hfld{i};
        j = find(tmpfld == ' ');
        tmpfld = tmpfld(1:j(1)-1);
        noerror = min(noerror,any(strcmp(tmpfld,gfld)));  %Noerror flag set to 0 if mismatch found
      end
    end
    if ~noerror  %Found case where HAVING field not in GROUP BY fields
      errordlg('HAVING clause references fields not found in GROUP BY clause.');
      set(findobj('Type','figure'),'Pointer','arrow')
      e = 1;
      return
    end
    
    %Get GROUP BY and ORDER BY fields clashes
    if ~isempty(odat)
      ofld = odat{1};
      for i = 1:length(ofld)
        tmpfld = ofld{i};
        j = find(tmpfld == ' ');
        tmpfld = tmpfld(1:j(1)-1);
        noerror = min(noerror,any(strcmp(tmpfld,gfld))); %Noerror flag set to 0 is mismatch found
      end
    end
    if ~noerror %Found case where ORDER BY fields not in GROUP BY fields
      errordlg('ORDER BY clauses references fields not found in GROUP BY clause.');
      set(findobj('Type','figure'),'Pointer','arrow')
      e = 1;
      return
    end
    
% case 'Execute' - see executequery.m (needed a script to get data to workspace
    
  case 'Exit'
    
    %Determine if unsaved query data in window
    sflag = get(findobj(gcf,'Tag','saveflag'),'Value');
    if sflag
      bname = questdlg('Save Current Query?','Unsaved Query Data...');
      switch bname
        case 'Yes'
          querycallbacks('Save');
        case 'No'
        case 'Cancel'
          set(findobj('Type','figure'),'Pointer','arrow')
          return
      end
    end  
    closereq 
    return
         
  case 'Group By...'
    
    %Cannot display GROUP BY dialog without any fieldname 
    fstr = get(fobj,'String');
    fval = get(fobj,'Value');
    if isempty(fstr) | isempty(fval)    %No table  or fields selected 
      errordlg('No fieldnames selected.');
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end
    
    %Need connection  and dmd to determine supported GROUP BY properties
    conn = loginconnect(sourcestr);
    if strcmp(class(conn.Handle),'double')
      set(findobj('Type','figure'),'Pointer','arrow')
      return    %Invalid connection, stop processing
    end
    d = dmd(conn);
        
    %Build GROUP BY clause dialog
    clausedialog('GROUP BY',bspc,bwid,bhgt);
    
    %Table fieldnames
    x = supports(d,'GroupByBeyondSelect') | supports(d,'GroupByUnrelated');
    displayfieldsbox('groupbyfields',bspc,bwid,bhgt,~x);
    close(conn)
    
    %Build edit boxes to define clause
    uicontrol('Style','text','String','Group key number',...
      'Position',[4*bspc+2*bwid 5*bspc+10*bhgt bwid bhgt]);
    uicontrol('Style','edit','Tag','sortkey','String','1',...
      'Tooltip','Entry number in clause list',...
      'Position',[4*bspc+2*bwid 3*bspc+9*bhgt bwid bhgt]);
    
    %Fill dialog with existing dialog
    filldialog('groupby');
    
  case 'groupclauses'
    
    %Find the object containing the clauses
    dlgstr = get(gcf,'Tag');
    switch dlgstr
      case 'WHERE'
        wobj = findobj(gcf,'Tag','whereclauses');    %whereclauses
      case 'SUBQDLG'
        wobj = findobj(gcf,'Tag','swhereclauses');    %subquery whereclauses
    end
    
    %Get the string and chosen clauses
    wstr = get(wobj,'String');
    wval = get(wobj,'Value');
    nvals = length(wval);
    
    %Add parenthesis
    if ~isempty(wval)
      wstr{wval(1)} = ['(' wstr{wval(1)}];
      %If logical operator in last clause, put ) before it
      i = findstr(wstr{wval(nvals)},' AND');
      if isempty(i)
        i = findstr(wstr{wval(nvals)},' OR');
      end
      if isempty(i)
        wstr{wval(nvals)} = [wstr{wval(nvals)} ') '];
      else
        wlen = length(wstr{wval(nvals)});
        wstr{wval(nvals)} = [wstr{wval(nvals)}(1:i-1) ' ) ' wstr{wval(nvals)}(i+1:wlen)];
      end
      set(wobj,'String',wstr)
    end
    
    %Update subquery sql statement if in subquery dialog
    switch dlgstr
      case 'SUBQDLG'
        subquery('fields')
    end
  case 'ungroupclauses'
    
    %Find the object containing the clauses
    dlgstr = get(gcf,'Tag');
    switch dlgstr
      case 'WHERE'
        wobj = findobj(gcf,'Tag','whereclauses');    %whereclauses
      case 'SUBQDLG'
        wobj = findobj(gcf,'Tag','swhereclauses');    %subquery whereclauses
    end
    
    %Get the string and chosen clauses
    wstr = get(wobj,'String');
    wval = get(wobj,'Value');
    nvals = length(wval);
    
    %Remove parenthesis
    if ~isempty(wval)
      if strcmp(wstr{wval(1)}(1),'(')
        wstr{wval(1)}(1) = [];
      else
        errordlg('First chosen clause not within parenthesis.')
        set(findobj('Type','figure'),'Pointer','arrow')
        return
      end
      
      %Get length of last clause and location of )'s
      wlen = length(wstr{wval(nvals)});
      i = findstr(')',wstr{wval(nvals)});
      
      %Find ), ) AND, or ) OR
      switch wstr{wval(nvals)}(max(i):wlen)
        case  {')',') ',')  ','))'}
          wstr{wval(nvals)}(max(i)) = [];
        case {') OR',') AND',') OR ',') AND '}
          wstr{wval(nvals)}(max(i)) = [];
        otherwise
          errordlg('Last chosen clause not within parenthesis.')
          set(findobj('Type','figure'),'Pointer','arrow')
          return
      end
      set(wobj,'String',wstr)
    end  
    
    %Update subquery sql statement if in subquery dialog
    switch dlgstr
      case 'SUBQDLG'
        subquery('fields')
    end

  case 'Having...'
    
    %Do not display HAVING dialog if WHERE dialog is open
    wobj = findobj('Tag','WHERE');
    if ~isempty(wobj)
      errordlg('Close WHERE clauses... dialog before building HAVING clause.')
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end

    %Need valid GROUP BY clause in order to create HAVING clause
    gudt = get(gobj,'Userdata');
    hudt = get(hobj,'Userdata');
    if isempty(gudt) & isempty(hudt) %Allow HAVING to open if existing HAVING clause
      errordlg('HAVING clause cannot be built without existing GROUP BY clause.');
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end
    
    %Build HAVING clause dialog
    clausedialog('HAVING',bspc,bwid,bhgt);
    
    %Selected table fieldnames
    displayfieldsbox('havingfields',bspc,bwid,bhgt,1);
    
    %Build conditions and operator uicontrols
    condandoper('HAVING',bspc,bwid,bhgt);
    
  case 'Help'
    
    qbhelp;
    
  case 'Load'
    
    %Load an SQL statement from a file
    [f,p] = uigetfile('*.qry','Load SQL Statement...');
    if f == 0
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end
    
    try  
      eval(['load ' p f ' -mat'])      
      
      sval = find(strcmp(get(sobj,'String'),sstr));    %Reset source
      if isempty(sval)
        errordlg(['Data source ' sstr ' not found.']);
        set(findobj('Type','figure'),'Pointer','arrow')
        return
      end
      set(sobj,'Value',sval);      
      querycallbacks('sources');   %Update tables based on data source
      
      for i = 1:length(tstr)
        tval(i) = find(strcmp(get(tobj,'String'),tstr{i}));    %Reset tables
      end  
      set(tobj,'Value',tval);
      querycallbacks('tables');    %Update fields based on tables
      
      for i = 1:length(fstr)
        fval(i) = find(strcmp(get(fobj,'String'),fstr{i}));    %Reset fields
      end  
      set(fobj,'Value',fval);
      querycallbacks('fields');    %Update SQL statement
      
      %Reset ALL/DISTINCT flag
      set(robj,{'Value'},aodval)
      
      %Get the clause data for saving
      set(wobj,'Userdata',wdat);
      set(gobj,'Userdata',gdat);
      set(hobj,'Userdata',hdat);
      set(oobj,'Userdata',odat);
      if ~isempty(wdat),set(findobj('Tag','wherelist'),'String',wdat{1}),end
      if ~isempty(gdat),set(findobj('Tag','groupbylist'),'String',gdat{1}),end
      if ~isempty(hdat),set(findobj('Tag','havinglist'),'String',hdat{1}),end
      if ~isempty(odat),set(findobj('Tag','orderbylist'),'String',odat{1}),end
      
      %Rebuild the SQL statement
      buildquery;
      
      %Display loaded query name in title bar
      set(gcf,'Name',['Visual Query Builder - ' f])
      
      %Unset save flag, buildquery sets it by default, does not apply for this action
      set(findobj(gcf,'Tag','saveflag'),'Value',0)
      
    catch
      
      errordlg(['Unable to load ' p f]);
      
    end
  
  case 'Order By...'
    
    %Cannot display ORDER BY dialog without any fieldnames    
    fstr = get(fobj,'String');
    if isempty(fstr)
      errordlg('No table selected.');
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end

    %Build ORDER BY clause dialog
    clausedialog('ORDER BY',bspc,bwid,bhgt);
    
    %Table fieldnames, if GROUP BY clause exists, only fields in 
    %GROUP BY clause are relevant
    gudt = get(gobj,'Userdata');
    if isempty(gudt)
      selflag = 0;
    else
      selflag = 1;
    end
    displayfieldsbox('orderbyfields',bspc,bwid,bhgt,selflag);
    
    %Build radio buttons and edit boxes to define clause
    uicontrol('Style','text','String','Sort key number',...
      'Position',[4*bspc+2*bwid 5*bspc+10*bhgt bwid bhgt]);
    uicontrol('Style','edit','Tag','sortkey','String','1',...
      'Tooltip','Entry number in clause list',...
      'Position',[4*bspc+2*bwid 3*bspc+9*bhgt bwid bhgt]);
    uicontrol('Style','text','String','Sort order',...
      'Position',[7*bspc+3*bwid 5*bspc+10*bhgt bwid bhgt]);
    uicontrol('Style','radiobutton','Tag','sortorder','Userdata','ASC',...
      'Tooltip','Sort field in ascending order',...
      'String','Ascending','Callback','ordergroupcallbacks','Value',1,...
      'Position',[7*bspc+3*bwid 5*bspc+9*bhgt bwid bhgt]);
    uicontrol('Style','radiobutton','Tag','sortorder','Userdata','DESC',...
      'Tooltip','Sort field in descending order',...
      'String','Descending','Callback','ordergroupcallbacks','Value',0,...
      'Position',[7*bspc+3*bwid 5*bspc+8*bhgt bwid bhgt]);
    
    %Fill dialog with existing dialog
    filldialog('orderby');
    
  case 'Preferences'
    
    %Build preferences dialog
    f = findobj('Tag','VQBPrefs');
    if isempty(f)
      f = figure('Tag','VQBPrefs','Name','Visual Query Builder Preferences',...
        'Numbertitle','off','Keypressfcn','querycallbacks(''PrefOK'')');
    else
      figure(f);
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end
    p = get(f,'Position');
        
    %Frame dimensions
    fwid1 = 5*bspc+3*bwid;
    fhgt1 = 4*bspc+5*bhgt;
    fwid2 = 2*bspc+bwid;
    fhgt2 = 5*bspc+4*bhgt;
    fwid3 = 3*bspc+fwid1+fwid2;
    fhgt3 = 3*bspc+fhgt1;
    
    %Reset figure position
    set(f,'Position',[p(1) p(2) 2*bspc+fwid3 bspc+fhgt3]);
    p = get(f,'Position');
    rgt = p(3);
    top = p(4);

    %Build base frame
    uicontrol('Enable','off','Position',[bspc top-fhgt3 fwid3 fhgt3]);
    
    %Build NULL data handling uicontrols
    prefs = setdbprefs;  %Get current settings
    uicontrol('Enable','off','Position',[2*bspc top-(6*bspc+5*bhgt) fwid1 fhgt1]);
    uicontrol('Style','text','String','Null data handling',...
      'Position',[3*bspc top-(bspc+bhgt) bwid bhgt]);
    uicontrol('Style','text','String','Read NULL strings as:','Fontweight','bold',...
      'Position',[4*bspc top-(2*bspc+2*bhgt) bwid bhgt]);
    uicontrol('Style','edit','Tag','NullStringRead','String',prefs.NullStringRead,...
      'Position',[5*bspc+2*bwid top-(bspc+2*bhgt) bwid bhgt]);
    uicontrol('Style','text','String','Read NULL numbers as:','Fontweight','bold',...
      'Position',[4*bspc top-(3*bspc+3*bhgt) bwid bhgt]);
        uicontrol('Style','edit','Tag','NullNumberRead','String',prefs.NullNumberRead,...
      'Position',[5*bspc+2*bwid top-(2*bspc+3*bhgt) bwid bhgt]);
    uicontrol('Style','text','String','written as NULL strings.','Fontweight','bold',...
      'Position',[5*bspc+bwid top-(4*bspc+4*bhgt) bwid bhgt]);
    uicontrol('Style','edit','Tag','NullStringWrite','String',prefs.NullStringWrite,...
      'Position',[4*bspc top-(3*bspc+4*bhgt) bwid bhgt]);
    uicontrol('Style','text','String','written as NULL numbers.','Fontweight','bold',...
      'Position',[5*bspc+bwid top-(5*bspc+5*bhgt) bwid bhgt]);
     uicontrol('Style','edit','Tag','NullNumberWrite','String',prefs.NullNumberWrite,...
      'Position',[4*bspc top-(4*bspc+5*bhgt) bwid bhgt]);
    
    %Build pushbutton uicontrols
    uicontrol('Enable','off','Position',[3*bspc+5*bspc+3*bwid top-(6*bspc+5*bhgt) fwid2 fhgt2]);
    uicontrol('String','Help','Callback','querycallbacks','Tag','PrefHelp',...
      'Position',[9*bspc+3*bwid top-(5*bspc+5*bhgt) bwid bhgt]);
    uicontrol('String','Cancel','Callback','querycallbacks','Tag','Cancel',...
      'Position',[9*bspc+3*bwid top-(4*bspc+4*bhgt) bwid bhgt]);
    uicontrol('String','OK','Callback','querycallbacks','Tag','PrefOK',...
      'Position',[9*bspc+3*bwid top-(3*bspc+3*bhgt) bwid bhgt]);
    uicontrol('String','Apply','Callback','querycallbacks','Tag','PrefApply',...
      'Position',[9*bspc+3*bwid top-(2*bspc+2*bhgt) bwid bhgt]);

    
    cleanupdialog;
    
  case 'PrefApply'
    
    nsr = get(findobj('Tag','NullStringRead'),'String');
    nnr = get(findobj('Tag','NullNumberRead'),'String');
    nsw = get(findobj('Tag','NullStringWrite'),'String');
    nnw = get(findobj('Tag','NullNumberWrite'),'String');
    
    %Trap bad settings
    if isempty(nnr)
      nnr = '[]';
    end
    
    setdbprefs({'NullStringRead';'NullNumberRead';'NullStringWrite';'NullNumberWrite'},...
      {nsr;nnr;nsw;nnw});
    
  case 'PrefHelp'
    
    qbhelp('Preferences')
    
  case 'PrefOK'
    
    querycallbacks('PrefApply')
    close
    
  case 'querydata'
    
    %Workspace variable listbox callback
    
    %Implement double click behavior (store time on first click)
    if (now - get(dobj,'Userdata')) > 5e-6
      set(dobj,'Userdata',now)
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end
    
    %Get selected variable from list
    dval = get(dobj,'Value');
    dstr = get(dobj,'String');
    varstr = dstr{dval};
    
    %String up to first space if chosen variable
    i = find(varstr == ' ');
    v = varstr(1:i(1));
    
    %Echo variable in command window
    evalin('base',v,'set(findobj(''Type'',''figure''),''Pointer'',''arrow''),error(lasterr)')
    
  case 'Save'
    
    %Save an SQL statement to a MAT file
    if isempty(querystr)
      errordlg('No SQL statement to save.')
      set(findobj('Type','figure'),'Pointer','arrow')  
      return
    end
    
    [f,p] = uiputfile('*.qry','Save SQL Statement...');
    if f == 0
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end
    
    %Get source, table and field information for saving
    sdat = get(sobj,{'String','Value'});
    sstr = sdat{1}{sdat{2}};
    tdat = get(tobj,{'String','Value'});
    tstr = {tdat{1}{tdat{2}}};
    fdat = get(fobj,{'String','Value'});
    fstr = {fdat{1}{fdat{2}}};
    
    %Get ALL/DISTINCT values
    aodval = get(robj,'Value');
    
    %Get the clause data for saving
    wdat = get(wobj,'Userdata');
    gdat = get(gobj,'Userdata');
    hdat = get(hobj,'Userdata');
    odat = get(oobj,'Userdata');
    
    %Append .qry to file if not given
    fl = length(f);
    if fl < 4 | ~strcmp(f(fl-3:fl),'.qry')
      f = [f '.qry'];
    end
    
    try 
      eval(['save ' p f ' sstr tstr fstr aodval wdat gdat hdat odat -mat'])
      set(findobj(gcf,'Tag','saveflag'),'Value',0)    %Reset save flag
      set(gcf,'Name',['Visual Query Builder - ' f])   %Show current name
    catch
      errordlg(['Unable to save query to ' p f]);
    end
    
  case 'sources'  
    
    %Determine if unsaved query data in window
    sflag = get(findobj(gcf,'Tag','saveflag'),'Value');
    if sflag
      bname = questdlg('Save Current Query?','Unsaved Query Data...');
      switch bname
        case 'Yes'
          querycallbacks('Save');
        case 'No'
          set(findobj(gcf,'Tag','saveflag'),'Value',0)    %Reset save flag
        case 'Cancel'
          set(findobj('Type','figure'),'Pointer','arrow')
          return
      end
    end  
   
    %Selecting a new data source erases any clause data
    set(wobj,'Userdata',[]);
    set(oobj,'Userdata',[]);
    set(gobj,'Userdata',[]);
    set(hobj,'Userdata',[]);
    
    %Set visual queue listboxes to empty strings
    set(findobj('Tag','wherelist'),'String',[])
    set(findobj('Tag','groupbylist'),'String',[])
    set(findobj('Tag','havinglist'),'String',[])
    set(findobj('Tag','orderbylist'),'String',[])

    %Make connection to chosen to source
    conn = loginconnect(sourcestr);
    if strcmp(class(conn.Handle),'double')
      set(findobj('Type','figure'),'Pointer','arrow')
      return    %Invalid connection, stop processing
    end
    
    %Get table information (for all schemas)
    d = dmd(conn);
    ctg = get(conn,'Catalog');
    tinfo = tables(d,ctg,'');
    tableType = tinfo(:,2);
    tableName = tinfo(:,1);
    
    %Table types of TABLE are desired tables
    i = find(~(strcmp({'SYSTEM TABLE'},tableType) | ...
               strcmp({'SYNONYM'},tableType)));
    table = tableName(i);
    
    %Determine if GROUP BY and HAVING clauses are supported
    x = supports(d,'GroupBy');
    if x 
      set(findobj(gcf,'Tag','Group By...'),'Enable','on')
      set(findobj(gcf,'Tag','Having...'),'Enable','on')
    else
      set(findobj(gcf,'Tag','Group By...'),'Enable','off')
      set(findobj(gcf,'Tag','Having...'),'Enable','off')
    end
    
    %Close connection
    close(conn);
    
    %Reset fields listbox, query, workspace variable and data string so incorrect fieldnames are not showing for chosen source
    set([fobj;qobj;vobj],'String',[])
    set(tobj,'String',table(:),'Value',[])
    
    %Set source object to allow for only data source selection
    set(sobj,'Max',1,'Value',1)   %Need to toggle value to get correct visual effect,
    set(sobj,'Value',sval)        %otherwise, source at end of list is scrolled of screen
        
  case 'tables'
    
    %Reset fields listbox, query, workspace variable and data string so incorrect fieldnames are not showing for chosen source
    set([fobj;qobj;vobj],'String',[])
    
    %Make connection to datasource
    dsval = get(sobj,'Value');
    dsstr = get(sobj,'String');
    datasource = dsstr{dsval};
    conn = loginconnect(datasource); 

    %Get selected tables 
    tval = get(tobj,'Value');
    tstr = get(tobj,'String');
    tablez = tstr(tval);
    
    %Check for spaces in table name
    for i = 1:length(tablez)
      tmptab = tablez{i};
      L = length(tmptab);
      i = find(tmptab == ' ');
      if any(i < L)
        errordlg('Blank spaces in tablenames are not supported.')
        set(findobj('Type','figure'),'Pointer','arrow')
        return
      end
    end
      
    %Get metadata of connection and get column names
    d = dmd(conn);
    L = length(tablez);
    fields = {};
    for i = 1:L
      
      table = tablez{i};  %Columns method returns more info, but is much slower
      ex = exec(conn,['select * from ' table]);
      ex = fetch(ex,1);
      tmp = columnnames(ex);
      eval(['tabfields = {' tmp '};'])
      close(ex); 
      
      %Prepend tablename if JOIN operation
      if length(tval) > 1
        for j = 1:length(tabfields)
          tabfields{j} = [table '.' tabfields{j}];
        end
        fields = [fields;tabfields'];
      else
        fields = tabfields';
      end
    end
     
    %Display fieldnames
    set(fobj,'String',fields,'Value',[]);
    
    %Close connection
    close(conn);
    
  case 'varlist'
    
    %Build string to display current workspace data 
    XxX = evalin('base','whos');
    ansstr = {};
    ans = ' ';  
    for ansi = 1:length(XxX)
      tmpans = [num2str(XxX(ansi).size(1)) 'x' num2str(XxX(ansi).size(2))];
      ansstr = [ansstr;...
         {sprintf([XxX(ansi).name ans(ones(1,22-length(XxX(ansi).name))) tmpans ans(ones(1,17-length(tmpans))) num2str(XxX(ansi).bytes)])}];
    end
    set(findobj('Tag','querydata'),'String',ansstr,'Value',[])

  case 'Where...'
    
    %Cannot display WHERE dialog without any fieldnames
    
    fstr = get(fobj,'String');
    if isempty(fstr)
      errordlg('No valid fieldnames displayed.');
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end
    
    %Do not display WHERE dialog if HAVING dialog is open
    hobj = findobj('Tag','HAVING');
    if ~isempty(hobj)
      errordlg('Close HAVING clauses... dialog before building WHERE clause.')
      set(findobj('Type','figure'),'Pointer','arrow')
      return
    end

    %Build WHERE clause dialog
    clausedialog('WHERE',bspc,bwid,bhgt);
        
    %Table fieldnames
    displayfieldsbox('wherefields',bspc,bwid,bhgt);
    
    %Build conditions and operator uicontrols
    condandoper('WHERE',bspc,bwid,bhgt);
    
end

cleanupdialog;
set(findobj('Type','figure'),'Pointer','arrow')


%Subfunctions

function x = clausedialog(tagname,bspc,bwid,bhgt);
%CLAUSEDIALOG Main clause dialog figure window.
%   CLAUSEDIALOG(TAGNAME,BSPC,BWID,BHGT) displays the main clause dialog window
%   for the given clause type, TAGNAME.   BSPC, BWID, and BHGT are the button space, 
%   width, and height parameters.  

fobj = findobj('Type','figure','Tag',tagname);
if ~isempty(fobj)
  close(fobj);
end

%Format tagname
ltag = lower(tagname);
i = find(ltag == ' ');
ltag(i) = [];

f = figure('Numbertitle','off','Menubar','none','Name',[tagname ' Clauses'],'Tag',tagname);
fp = get(gcf,'Position');
set(gcf,'Position',[fp(1) fp(2) 7*bspc+7*bwid 6*bspc+11*bhgt]);

%Build dialog frames
uicontrol('Enable','off','Position',[bspc 4*bspc+5*bhgt 5*bspc+7*bwid 6*bhgt]);

%Set callback function and position of Edit button 
if any(strcmp(ltag,{'where','having'}))
  cbfunction = 'conditioncallbacks';
  editpos = bspc+bhgt;
else
  cbfunction = 'ordergroupcallbacks';
  editpos = 0;
end

%Build list box to show clauses
uicontrol('Enable','off','Position',[bspc bspc 5*bspc+7*bwid bspc+5*bhgt]);
uicontrol('Style','listbox','Tag',[ltag 'clauses'],'Callback',cbfunction,...
  'Tooltip','List of clauses','Max',2,'Value',[],...
  'Position',[2*bspc 2*bspc 5*bwid bspc+4*bhgt]);
uicontrol('Style','text','String','Current clauses','Position',[2*bspc 3*bspc+4*bhgt bwid bhgt]);

%Build pushbuttons
uicontrol('String','Help','Tag','Help','Callback','qbhelp(get(gcf,''Tag''))',...
  'Position',[6*bspc+6*bwid 6*bspc+6*bhgt bwid-bspc bhgt]);
uicontrol('String','Apply','Callback',cbfunction,'Tag','Apply',...
  'Tooltip','Add clause',...
  'Position',[6*bspc+6*bwid 5*bspc+5*bhgt bwid-bspc bhgt]);
uicontrol('String','Edit','Callback',cbfunction,'Tag','Edit','Userdata',0,...
  'Tooltip','Edit clause',...
  'Position',[5*bspc+6*bwid 5*bspc+3*bhgt bwid bhgt]);
%Add Group clauses button if Where/Having dialog
if editpos
  uicontrol('String','Group','Callback','querycallbacks(''groupclauses'')','Tag','Group',...
    'Tooltip','Group clauses',...
    'Position',[3*bspc+5*bwid 4*bspc+2*bhgt bwid bhgt]);
  uicontrol('String','Ungroup','Callback','querycallbacks(''ungroupclauses'')','Tag','Ungroup',...
    'Tooltip','Ungroup clauses',...
    'Position',[3*bspc+5*bwid 3*bspc+bhgt bwid bhgt])
end
uicontrol('String','Delete','Callback',cbfunction,'Tag','Delete',...
  'Tooltip','Delete clause',...
  'Position',[5*bspc+6*bwid 4*bspc+2*bhgt bwid bhgt]);
uicontrol('String','Cancel','Callback',cbfunction,'Tag','Cancel',...
  'Tooltip','Close dialog without changes',...
  'Position',[5*bspc+6*bwid 3*bspc+bhgt bwid bhgt]);
uicontrol('String','OK','Callback',cbfunction,'Tag','OK',...
  'Tooltip','Close dialog and build SQL statement',...
  'Position',[5*bspc+6*bwid 2*bspc bwid bhgt]);

function displayfieldsbox(tagname,bspc,bwid,bhgt,selflag)
%DISPLAYFIELDSBOX Displays current fields in the select clause dialog box
%   DISPLAYFIELDSBOX(TAGNAME,BSPC,BWID,BHGT,SELFLAG) displays the current fields in a list box in the
%   current select clause dialog box, ie WHERE, ORDER BY.  TAGNAME is the tag assigned 
%   to the listbox.  BSPC, BWID, and BHGT are the button space, width, and height parameters.
%   SELFLAG determines whether all valid fields are displayed or only those in the SELECT statement.
%   Using a GROUP BY clause makes only SELECTed fields valid for ORDER BY clauses.

%Default selflag is 0
if nargin < 5
  selflag = 0;
end

%Get all valid fields or selected fields depending on flag
fobj = findobj('Tag','fields');
fstr = get(fobj,'String');
fval = get(fobj,'Value');
if selflag
  fldstr = fstr(fval);  
else 
  fldstr = fstr;
end

%Build uicontrols
uicontrol('Style','text','String','Fields',...
  'Position',[2*bspc 5*bspc+10*bhgt bwid bhgt]);
uicontrol('Style','listbox','String',fldstr,...
  'Tooltip','Valid fields',...
   'Tag',tagname,'Max',1,'Position',[2*bspc 5*bspc+5*bhgt 2*bwid 5*bhgt]);

function condandoper(tagname,bspc,bwid,bhgt)
%CONDANDOPER Condition and operator uicontrols for WHERE and HAVING clause dialogs.
%   CONDANDOPER(TAGNAME,BSPC,BWID,BHGT) builds the condition and operator uicontrols
%   for the WHERE and HAVING clause dialogs.  BSPC, BWID, and BHGT are the button space, 
%   width, and height parameters.

%Build radio buttons and edit boxes to define clause
uicontrol('Style','text','String','Condition',...
  'Position',[4*bspc+2*bwid 5*bspc+10*bhgt bwid bhgt]);
uicontrol('Style','radiobutton','Tag','rcond',...
  'String','Like','Callback','conditioncallbacks',...
  'Position',[4*bspc+2*bwid 6*bspc+5*bhgt bwid bhgt]);
uicontrol('Style','edit','Tag','matchstring',...
  'Tooltip','Comparison string or value ie ''abc'' or 1',...
  'Position',[4*bspc+3*bwid 6*bspc+5*bhgt bspc+2*bwid bhgt]);
uicontrol('Style','radiobutton','Tag','rcond',...
  'String','Is null','Callback','conditioncallbacks',...
  'Position',[4*bspc+2*bwid 6*bspc+6*bhgt bwid bhgt]);
uicontrol('Style','radiobutton','Tag','rcond',...
  'String','In','Callback','conditioncallbacks',...
  'Position',[4*bspc+2*bwid 6*bspc+7*bhgt bwid bhgt]);
uicontrol('Style','edit','Tag','instring',...
  'Tooltip','List of comparison strings or values ie (''abc'',''cde'') or (1,2)',...
  'Position',[4*bspc+3*bwid 6*bspc+7*bhgt bspc+2*bwid bhgt]);
uicontrol('Style','radiobutton','Tag','rcond',...
  'String','Between','Callback','conditioncallbacks',...
  'Position',[4*bspc+2*bwid 6*bspc+8*bhgt bwid bhgt]);
uicontrol('Style','edit','Tag','lwstring',...
  'Tooltip','Lower bound comparison string or value ie ''abc'' or 1',...
  'Position',[4*bspc+3*bwid 6*bspc+8*bhgt bwid bhgt]);
uicontrol('Style','edit','Tag','upstring',...
  'Tooltip','Upper bound comparison string or value ie ''cde'' or 2',...
  'Position',[5*bspc+4*bwid 6*bspc+8*bhgt bwid bhgt]);
uicontrol('Style','radiobutton','Tag','rcond','Value',1,...
  'String','Relation','Callback','conditioncallbacks',...
  'Position',[4*bspc+2*bwid 6*bspc+9*bhgt bwid bhgt]);
uicontrol('Style','popupmenu','String',{'=','<','>','>=','<=','<>'},'Tag','relation',...
  'Tooltip','Relational operator',...
  'Position',[4*bspc+3*bwid 6*bspc+9*bhgt bwid bhgt]);
uicontrol('Style','edit','Tag','relstring',...
  'Tooltip','Comparison string or value ie ''abc'' or 1',...
  'Position',[5*bspc+4*bwid 6*bspc+9*bhgt bwid bhgt]);

%Build AND/OR radio buttons, add subquery button
uicontrol('String','Subquery...','Callback','subquery(''dialog'')',...
    'Tooltip','Build a subquery',...
    'Position',[6*bspc+6*bwid 6*bspc+9*bhgt bwid-bspc bhgt]);
uicontrol('Style','text','String','Operator',...
  'Position',[7*bspc+5*bwid 5*bspc+10*bhgt bwid bhgt]);
uicontrol('Style','radiobutton','Tag','andor',...
  'Tooltip','AND operator',...
  'String','AND','Callback','conditioncallbacks','Value',0,...
  'Position',[7*bspc+5*bwid 6*bspc+9*bhgt bwid-2*bspc bhgt]);
uicontrol('Style','radiobutton','Tag','andor',...
  'Tooltip','OR operator',...
  'String','OR','Callback','conditioncallbacks','Value',0,...
  'Position',[7*bspc+5*bwid 6*bspc+8*bhgt bwid-2*bspc bhgt]);
uicontrol('Style','radiobutton','Tag','andor',...
  'Tooltip','No operator',...
  'String','None','Callback','conditioncallbacks','Value',1,...
  'Position',[7*bspc+5*bwid 6*bspc+7*bhgt bwid-2*bspc bhgt]);
 
%Fill dialog with existing dialog
ewcobj = findobj('Tag',[lower(tagname) 'clausedata']);
ewcdat = get(ewcobj,'Userdata');
if ~isempty(ewcdat)
  wcstr = ewcdat{1};
  wcdat = ewcdat{2};
  wclobj = findobj('Tag',[lower(tagname) 'clauses']);
  set(wclobj,'String',wcstr,'Userdata',wcdat)
end

function filldialog(tagname)
%FILLDIALOG Group By and Order By dialog parameters.
%   FILLDIALOG(TAGNAME) fill the Group By or Order By dialogs given a
%   TAGNAME string identifying which dialog is being opened.

%Fill dialog with existing dialog
eocobj = findobj('Tag',[tagname 'clausedata']);
eocdat = get(eocobj,'Userdata');
if ~isempty(eocdat)
  ocstr = eocdat{1};
  ocdat = eocdat{2};
  oclobj = findobj('Tag',[tagname 'clauses']);
  set(oclobj,'String',ocstr,'Userdata',ocdat)
end

function cleanupdialog()
%CLEANUPDIALOG Visual enhancement of dialog.

%Set colors and alignment
e = findobj(gcf,'Style','edit');
l = findobj(gcf,'Style','listbox');
p = findobj(gcf,'Style','popupmenu');
set([e;l;p],'Backgroundcolor','white','Horizontalalignment','left')
dbc = get(0,'Defaultuicontrolbackgroundcolor');
set(gcf,'Color',dbc)

%Make text boxes proper width
textuis = findobj(gcf,'Style','text');
for i = 1:length(textuis)
  pos = get(textuis(i),'Position');
  ext = get(textuis(i),'Extent');
  set(textuis(i),'Position',[pos(1) pos(2) ext(3) pos(4)])
end
set(textuis,'Backgroundcolor',dbc)

%Normalize units
set(findobj(gcf,'Type','uicontrol'),'Units','normal')

