/*
This file is part of Bolixo.
Bolixo is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bolixo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bolixo. If not, see .
*/
#include
#include
#include
#include "util.h"
#include "webtabs.h"
#include "../bolixo.m"
#include
#include
#include
using namespace std;
static W_UNSIGNED w_webtab("webtab");
static W_UNSIGNED w_subtab("subtab"); // For mobile and mobile_view, it selects the visible area
static W_SSTRING w_webtab_add("webtab_add");
/*
Set the subtab from the session manager. The subtab is the visible area. Normally a webtab display
3 areas side by side. The documents area, the docmain and doctype2 (and potentially other).
In mobile mode, only one area is displayed at once and you swipe between areas. The w_subtab
variable and the session control which area is displayed.
*/
void webtabs_set_subtab (unsigned val)
{
if (!w_subtab.isset()) w_subtab = val;
}
/*
Get the current subtab value to update the session manager.
*/
unsigned webtabs_get_subtab ()
{
return w_subtab;
}
/*
Return true if the webtab_add is set
*/
bool webtabs_adding ()
{
return w_webtab_add.isset();
}
void webtabs_forcevar(PARAM_STRING val)
{
tlmpweb_forcevar(w_webtab_add,val.ptr);
}
bool _F_webtabs::selected(PARAM_STRING id)
{
return selected_id == id.ptr;
}
void _F_webtabs::setid (PARAM_STRING id)
{
if (curtab->tab != id.ptr){
curtab->tab = id.ptr;
changed = true;
}
}
void _F_webtabs::sethelp ()
{
help_id[0] = "help";
help_title[0] = MSG_U(I_HELP,"Help");
}
void _F_webtabs::sethelp (PARAM_STRING id, PARAM_STRING title)
{
help_id[0] = id.ptr;
help_title[0] = title.ptr;
}
void _F_webtabs::sethelp2 ()
{
help_id[1] = "help2";
help_title[1] = MSG_R(I_HELP);
}
void _F_webtabs::sethelp2 (PARAM_STRING id, PARAM_STRING title)
{
help_id[1] = id.ptr;
help_title[1] = title.ptr;
}
void _F_webtabs::settitle (PARAM_STRING title)
{
if (curtab->title != title.ptr){
curtab->title = title.ptr;
changed = true;
}
}
void _F_webtabs::setstate (PARAM_STRING state)
{
if (curtab->state != state.ptr){
curtab->state = state.ptr;
changed = true;
}
}
const char *_F_webtabs::getstate () const
{
return curtab->state.c_str();
}
/*
Change the definition of a tab, if it exists.
This is generally used when a document is renamed, so the corresponding tab must be fixed.
Now, renaming a document not currently displayed is supported as well (this function does nothing then and returns -1).
Return -1 if the tab was not found. Return 0 otherwise.
If the item renamed was a folder, this function may affect several tabs.
*/
int _F_webtabs::replacetab(PARAM_STRING oldid, PARAM_STRING newid, PARAM_STRING title)
{
int ret = -1;
// We have to loop through all the tabs. We may have to rename a tab for the folder itself
// and tabs for documents in that folder.
for (auto &t:*tabs){
const char *pt;
if (t.tab == oldid.ptr){
t.tab = newid.ptr;
t.title = title.ptr;
changed = true;
ret = 0;
}else if (is_start_any_of(t.tab,pt,oldid.ptr) && pt[0] == '/'){
// OK, we are renaming a folder and this tab correspond to a document in that folder
t.tab = string_f("%s%s",newid.ptr,pt);
// The title of the document (the name of the file) is not changed
changed = true;
ret = 0;
}
}
return ret;
}
void _F_webtabs::redotab ()
{
redo = true;
}
void _F_webtabs::addtab (PARAM_STRING id, PARAM_STRING title_txt)
{
const char *ptid = id.ptr;
const char *pt = ptid;
WEBTAB_TYPE type = WEBTAB_TYPE1;
if (isdigit(pt[0]) && pt[1] == ':'){
type = (WEBTAB_TYPE)(pt[0]-'1');
pt+=2;
ptid = pt;
}
w_subtab=type+1;
string title;
if (title_txt.ptr[0] == '\0'){
title = ptid;
}else{
title = title_txt.ptr;
for (auto &s:title) if (s == '+') s=' ';
}
bool found = false;
for (auto &t:*tabs){
if (t.type == type){
if (t.tab == ptid){
found = true;
t.selorder = ++maxsel;
selected_id = t.tab;
added_tab = type;
}
}
}
if (!found){
added_tab = type;
WEBTAB w(type,ptid,title,"");
w.selorder = ++maxsel;
// New tabs are added in added_tabs and then moved to the main tabs table.
// because addtab may be called while the main tabs table is being processed.
// commit_addtab() is called to complete the work.
added_tabs.push_back(w);
selected_id = ptid;
}
changed = true;
}
void _F_webtabs::commit_addtab()
{
for (auto &t:added_tabs) tabs->push_back(t);
added_tabs.clear();
// hack, do not sort the first locked tabs.
for (auto t=tabs->begin(); t != tabs->end(); t++){
if (!t->locked){
sort (t,tabs->end());
break;
}
}
}
static unsigned webtab_href_c (PARAM_STRING id_suffix, const char *title, int tab, int nox, const char *color, const char *color_in, bool close)
{
unsigned ret = 0;
string hidden;
form_gethidden(hidden);
const char *sep = hidden.size() > 0 ? "&" : "";
string href = string_f ("%s?%s%swebtab=%d",tlmpweb_curpage(),hidden.c_str(),sep,tab);
if (nox != 0){
string xref = string_f ("%s?%s%swebtab=%d",tlmpweb_curpage(),hidden.c_str(),sep,nox);
ret = draw_tab (id_suffix.ptr,0,color,color_in,close,title,true,href,xref);
}else{
ret = draw_tab (id_suffix.ptr,0,color,color_in,close,title,href);
}
return ret;
}
static unsigned webtab_aref_selected (PARAM_STRING id_suffix, const char *title, int tab, int nox)
{
return webtab_href_c (id_suffix,title,tab,nox,"white","#F0F0F0",false);
}
static unsigned webtab_aref (PARAM_STRING id_suffix, const char *title, int tab, int nox, bool notify)
{
const char *color = notify ? "orange" : "#EAEAEA";
return webtab_href_c (id_suffix, title,tab,nox,color,"#E0E0E0",true);
}
void _F_webtabs::documents()
{
}
void _F_webtabs::doctype2(const char *id, const char *formid, const char *state, const char *title, unsigned width, unsigned height)
{
}
void _F_webtabs::doctype3(const char *id, const char *formid, const char *state, unsigned width, unsigned height)
{
}
void _F_webtabs::doctype4(const char *id, const char *formid, const char *state, unsigned width, unsigned height)
{
}
void _F_webtabs::init()
{
}
struct TABSEL {
string id;
unsigned no; // Position in tabs
TABSEL(){
no = 0;
}
};
static void webtabs(_F_webtabs &c,
const char *name,
map &alltabs,
bool may_delete,
unsigned size[5])
{
bool is_mobile = tlmpweb_ismobile();
WEBTAB_CTRL &ctrl = alltabs[name];
c.tabs = &ctrl.tabs;
glocal bool may_delete = may_delete;
c.maxsel = -1;
for (auto &t:ctrl.tabs){
if (t.selorder > c.maxsel) c.maxsel = t.selorder;
}
c.selected_id = "";
c.init();
if (w_webtab_add.isset()){
// We are either adding a new tab or selecting an existing one.
// While we look if the tab already exist, we de-select the others
// The variable may contains two selections separated by a comma. This is
// used by the documentation to jump into the application and bring
// with it the help screen
vector tb;
str_splitline(w_webtab_add.c_str(),',',tb);
for (auto &s:tb){
const char *ptid = s.c_str();
const char *title = ptid;
string id;
const char *pt = strchr(ptid,'~');
if (pt != NULL){
id = string(ptid,pt-ptid);
ptid = id.c_str();
title = pt+1;
}
c.addtab (ptid,title);
}
c.commit_addtab();
}
if (may_delete && w_webtab > 0 && (w_webtab & 1)==0){
// A click on the X to delete one tab
unsigned delete_tab = (w_webtab-1) / 2;
unsigned nbtab = c.tabs->size();
if (delete_tab < nbtab){
WEBTAB &tab = (*c.tabs)[delete_tab];
WEBTAB_TYPE type = tab.type;
const char *id = tab.tab.c_str();
tlmpweb_deleteform(id);
c.tabs->erase (c.tabs->begin()+delete_tab);
w_webtab = 0;
// Count the number of TABS remaining of this type
// Ajust the offset to have one visible
// Find the new current tab (the one with the hight sel_order)
unsigned count=0;
int max_t = 0;
for (auto &t:*c.tabs){
if (t.type == type){
count++;
if (t.selorder > max_t){
c.selected_id = t.tab;
max_t = t.selorder;
}
}
}
unsigned &offset = ctrl.offsets[type];
if (offset > 0 && count <= offset) offset--;
c.changed = true;
}
}
// Manage the left and right arrows and small swipe (mobile)
if (w_webtab >= 1000 && w_webtab < 1004){
unsigned &v = ctrl.offsets[w_webtab-1000];
if (v > 0) v--;
c.changed = true;
}else if (w_webtab >= 2000 && w_webtab < 2004){
ctrl.offsets[w_webtab-2000]++;
c.changed = true;
}else if (is_any_of(w_webtab,3000u,4000u) && w_subtab.getval() > 0){
// Small swipe for the mobile version
unsigned type = w_subtab-1;
vector prec;
int maxorder = 0;
unsigned maxpos = 0;
for (auto &t:ctrl.tabs){
if (t.type == type){
prec.push_back(&t);
if (t.selorder > maxorder){
maxorder = t.selorder;
maxpos = prec.size()-1;
}
}
}
if (w_webtab == 3000){
if (maxpos > 0){
if (ctrl.offsets[type] == maxpos) ctrl.offsets[type]--;
prec[maxpos-1]->selorder=maxorder+1;
c.changed = true;
}
}else if (w_webtab == 4000){
if (maxpos < prec.size()-1){
if (ctrl.offsets[type] == maxpos) ctrl.offsets[type]++;
prec[maxpos+1]->selorder=maxorder+1;
c.changed = true;
}
}
}
glocal WEBTAB_CTRL *ctrl = &ctrl;
glocal vector *tabs = c.tabs;
glocal bool changed = false;
// w_webtab let the user select the active tab, in any type
{
unsigned no = 1;
struct MAXTAB{
unsigned no;
int selorder;
MAXTAB(){
no = 0;
selorder = -1;
}
} maxtabs[4];
for (auto &t:ctrl.tabs){
MAXTAB &maxt = maxtabs[t.type];
if (t.selorder > maxt.selorder){
maxt.no = no;
maxt.selorder = t.selorder;
}
no += 2;
}
no = 1;
for (auto &t:ctrl.tabs){
if (w_webtab == no){
// Check if this is already the active tab
MAXTAB &maxt = maxtabs[t.type];
if (t.selorder != maxt.selorder || no != maxt.no){
t.selorder = ++c.maxsel;
c.changed = true;
}
c.selected_id = t.tab;
break;
}
no+=2;
}
}
bool draw_ok = true;
if (!tlmpweb_get_output_state()){
// PATCH
// This is a trick to allow the webtable::click system to work
// inside a vframe2h. We assume this is only used on the right side for now.
draw_ok = false;
}
// We draw the tabs content before drawing the tabs because the tabs may change
// the content of the tab
vector lines[4];
c.added_tab = -1;
/*
This is a little magic thing here... The typeorder variable below.
(little note. When the webtabs system was designed, we tought that 4 areas could be needed. It never happened.
We use 2 or 3 area:
Area 0 generally holds a list (groups, projects).
Area 1 holds tabs of the viewed items in area 0.
Area 2 holds tabs of viewed document in area 1.
Area 3, so far has not been used.
)
Normally, the tab area are processed in reverse order. The idea is that some event in one area (say the rightmost)
might influence the presentation of the documents on the left. For example, in one project tab, you create a
document. After entering some content, you hit save. Now 2 things happen. The document is created in the proper
folder and the tab name changes.
Since the folder content will be printed after the document event happen (save), it will properly show the new document name.
This is what the typeorder does.
Now, what about the dropdown menus. Without the reordering below for dropdown (webtable_is_dropdown()),
something spectacular goes on.
Here is an example. Go in the public project and create a document and save it. Now while the document is
displayed, use the right click to bring the dropdown menu. Select the "preview help". Now click back on your
document. Oops!!! The document is empty. It is still there in the database, but on the screen, it is empty.
Close the tab and click on the document in the folder and it will appear normally.
What is going on ?
Here is how documents/forms are preserved while you navigate in bolixo. Each time you click on something, all
forms are submitted silently (see the formsubmit javascript function). Once received, they are stored in the
temp database uinsg each active form id and the sessionid.
After every action, (once all HTML has been sent) the list of active forms are stored in the session manager. This list
is used for the next "formsubmit".
During the HTML production (server side), each visited form register itself as an active form. So here is the sequence
with the dropdown example below with order 3,2,1,0.
1-formsubmit is executed on the browser. It sends all active form, including the content of the document.
2-Since the document is visible in the right section, it is displayed first (the HTML is produced) and
it register itself as one active form.
3-Later the dropdown action is perform. It request to create a new tab in the right section, hiding
the tab of the document. The HTML associated with the document form is simply discared.
4-The new page appears with the help tab.
5-You click back on the document. formsubmit is executed and nothing is sent back from the browser
about this document, since it is not visible (the browser has no clue it even exists).
6-The forms are received on the server side and saved to the temp database. At step 2, the document
has registered itself as one active form, so the system save the content in the database. Empty
content.
So by changing the evaluation order when a dropdown is used, we evaluate the dropdown before the
right section is evaluated. so the document form is never registered as active.
*/
deque typeorder;
if (webtable_is_dropdown()){
typeorder={0,1,2,3};
}else{
typeorder={3,2,1,0};
}
bool mobile_view = is_mobile || tlmpweb_screenwidth() < 900;
struct TABSPEC{
unsigned width=0,height=0;
};
TABSPEC tbspec[5];
tlmpweb_delay_deleteform(true);
while (typeorder.size() > 0){
FORM_HIDDEN t(w_subtab);
const unsigned type = typeorder.front();
typeorder.pop_front();
WEBTAB *ptt = nullptr;
{
int maxorder = -1;
unsigned no = 1;
for (auto &t:*c.tabs){
if (t.type == type){
if (t.selorder > maxorder){
ptt = &t;
maxorder = t.selorder;
}
}
no+=2;
}
}
if (ptt != nullptr){
if (mobile_view && type != w_subtab-1) tlmpweb_visibletab(false);
lines[type].clear();
if (draw_ok) tlmpweb_pushgrab(lines[type]);
while (1){
unsigned no=1;
for (auto &t:*c.tabs){
if (&t == ptt) w_webtab = no;
no += 2;
}
const char *id = ptt->tab.c_str();
const char *formid = id;
const char *end = strchr(id,WEBTAB_MARK);
string tmp;
if (end != NULL){
tmp = string(id,end-id);
id = tmp.c_str();
}
c.curtab = ptt;
const char *state = ptt->state.c_str();
TABSPEC &spec = tbspec[type];
WEBID tabname(string_f("%s-tab%u",name,type));
tlmpweb_gettablegeometry(tabname,spec.height,spec.width);
if (spec.width == 0){
// Select some default so the first display (there will be a redraw) does not
// cause to much visual side-effect.
spec.width = tlmpweb_screenwidth()-100; // No need to correct TAB selection since
// we do not know the exact TAB width
spec.height = tlmpweb_screenheight()/2;
if (!is_mobile) spec.width /=2;
}
if (type == WEBTAB_TYPE1){
c.docmain(id,formid,state,spec.width,spec.height);
}else if (type == WEBTAB_TYPE2){
c.doctype2(id,formid,state,ptt->title.c_str(),spec.width,spec.height);
}else if (type == WEBTAB_TYPE3){
c.doctype3(id,formid,state,spec.width,spec.height);
}else if (type == WEBTAB_TYPE4){
c.doctype4(id,formid,state,spec.width,spec.height);
}else{
c.docmain(id,id,state,spec.width,spec.height);
}
// tlmp_warning ("webtabs type=%d id=%s draw_ok=%d redo=%d added_tab=%d",type,id,draw_ok,c.redo,c.added_tab);
if (!draw_ok || !c.redo){
break;
}
c.redo = false;
lines[type].clear();
}
if (draw_ok) tlmpweb_popgrab();
if (mobile_view && type != w_subtab-1) tlmpweb_visibletab(true);
}
if (c.added_tab != -1){
// We have to replay the tab
typeorder.push_back(c.added_tab);
c.added_tab = -1;
c.commit_addtab();
tlmpweb_clear_deletedforms();
}else{
tlmpweb_exec_deleteform();
}
}
tlmpweb_delay_deleteform(false);
if (draw_ok){
DIV sc; sc.w(100).dispflex().flowrow().align("stetch").bg("white").print();
vector docs;
tlmpweb_pushgrab(docs);
tlmpweb_visibletab(!mobile_view || w_subtab==0);
c.documents();
tlmpweb_visibletab(true);
tlmpweb_popgrab();
const unsigned margin = 5;
static const char *mcolor = "white"; //"#F3F3F3";
if (docs.size() > 0){
if (!mobile_view || w_subtab==0){
unsigned sz = mobile_view ? 100 : size[0];
DIV d("tabs","tabdoc"); d.flexfixe().bg(mcolor).align("stretch").w(sz).h(60).margins(margin,margin).print();
htmlout(docs);
}
}else if (w_subtab == 0){
w_subtab = 1;
}
glocal c;
bool putborder = true; // On each side of the first tab only
const unsigned side=1;
if (mobile_view && w_subtab > 0){
string hidden;
form_gethidden(hidden);
const char *sep = hidden.size() > 0 ? "&" : "";
string href = string_f ("%s?%s%ssubtab=%u",tlmpweb_curpage(),hidden.c_str(),sep,w_subtab-1);
if (is_mobile){
string href1 = string_f ("%s?%s%swebtab=3000&subtab=%u",tlmpweb_curpage(),hidden.c_str(),sep,w_subtab.getval());
htmlprintf ("\n",href.c_str(),href1.c_str());
}else{
DIV dc("tabs"); dc.flexfixe().w(side).align("stretch").bg("lightblue").print();
htmlprintf ("\n",href.c_str());
DIV ddc; ddc.w(100).h(100).bg("lightgray").print();
ddc.end();
htmlout ("\n");
}
}
FORM_HIDDEN t(w_subtab);
for (unsigned type=0; type <4; type++){
glocal const char *help_id = glocal.c.help_id[type].c_str();
glocal const char *help_title = glocal.c.help_title[type].c_str();
unsigned sz = size[type+1];
if (mobile_view){
if (type == w_subtab-1){
if (is_mobile){
sz = 100;
}else if (w_subtab == 1){
sz = 100-side-side;
}else{
sz = 100-side;
}
}else{
sz = 0;
}
}
//tlmp_error ("type=%u sz=%u subtab=%u\n",type,sz,w_subtab.getval());
if (sz != 0){
glocal unsigned tab_width = tbspec[type].width;
DIV dc; dc.flexfixe().w(sz).align("stretch").bg("white").print();
WEBID tabname(string_f("%s-tab%u",name,type));
DIV cc("tabs",tabname.c_str()); cc.dispflex().flowcol().bg(mcolor);
if (putborder){
cc.borderleft(1,"black");
cc.borderright(1,"black");
putborder = false;
}
cc.print();
// Set an internal margin, inside borders
DIV m; m.margins(margin,margin).bg("white").print();
DIV top("","tabs"); top.align("flex-start").flexfixe().print();
glocal unsigned type = type;
glocal WEBTAB *ptsel = NULL;
{
int maxorder = -1;
for (auto &t:*c.tabs){
if (t.type == type){
if (t.selorder > maxorder){
glocal.ptsel = &t;
maxorder = t.selorder;
}
}
}
}
(0,"white");
{
unsigned skip = glocal.ctrl->offsets[glocal.type];
// Check if the current tab is hidden to the left (< skip)
unsigned curtab = 0;
for (auto &t:*glocal.tabs){
if (t.type == glocal.type){
if (&t == glocal.ptsel){
break;
}
curtab++;
}
}
if (curtab < skip){
glocal.ctrl->offsets[glocal.type] = curtab;
glocal.changed = true;
}
}
string hidden;
form_gethidden(hidden);
const char *sep = hidden.size() > 0 ? "&" : "";
while (1){
// We do a loop until the "current" tab is fully visible
unsigned no = 1;
unsigned skip = glocal.ctrl->offsets[glocal.type];
bool redo = false;
{
string href = string_f ("%s?%s%swebtab=%d",tlmpweb_curpage(),hidden.c_str(),sep,1000+glocal.type);
drawleftarrow(href,skip>0);
}
unsigned nbprinted = 0;
unsigned width = 50; // Reserve space for left and right arrows
for (auto &t:*glocal.tabs){
if (t.type == glocal.type){
if (skip > 0){
skip--;
}else{
nbprinted++;
split();
const char *name = t.title.c_str();
if (name[0] == '\0'){
name = t.tab.c_str();
}
if (isdigit(name[0]) && name[1] == ':') name += 2;
int nodel = 0;
if (!t.locked && glocal.may_delete){
nodel = no+1;
}
if (&t == glocal.ptsel){
width += webtab_aref_selected (t.id,name,no,nodel);
if (width > glocal.tab_width){
glocal.ctrl->offsets[glocal.type]++;
glocal.changed = true;
redo = true;
break;
}
}else{
width += webtab_aref (t.id,name,no,nodel,t.notify);
}
}
}
no += 2;
}
{
string href = string_f ("%s?%s%swebtab=%d",tlmpweb_curpage(),hidden.c_str(),sep,2000+glocal.type);
drawrightarrow(href,width>glocal.tab_width);
}
if (glocal.tab_width == 0){
// tab unknown
drawendline(2000);
}else if (width < glocal.tab_width){
unsigned linewidth = glocal.tab_width-width;
if (glocal.help_id[0] != '\0') linewidth -= tlmpweb_helpsize();
drawendline(linewidth);
}
if (!redo) break;
reset();
}
if (glocal.help_id[0] != '\0'){
split();
url_self (string_f("webtab_add=2:%s~%s",glocal.help_id,glocal.help_title),tlmpweb_helpsvg().c_str());
}
top.end();
DIV mi; mi.bg("white").align("flex-start").flexgrow().print();
htmlout (lines[type]);
glocal.help_id = "";
}
}
if (mobile_view){
string hidden;
form_gethidden(hidden);
const char *sep = hidden.size() > 0 ? "&" : "";
if (w_subtab == 2){
// On the last TAB, only small right swipe are meaningfull
if (is_mobile){
string href1 = string_f ("%s?%s%swebtab=4000&subtab=%u",tlmpweb_curpage(),hidden.c_str(),sep,w_subtab.getval());
htmlprintf ("\n",href1.c_str());
}
}else{
string href = string_f ("%s?%s%ssubtab=%u",tlmpweb_curpage(),hidden.c_str(),sep,w_subtab+1);
if (is_mobile){
string href1 = string_f ("%s?%s%swebtab=4000&subtab=%u",tlmpweb_curpage(),hidden.c_str(),sep,w_subtab.getval());
htmlprintf ("\n",href.c_str(),href1.c_str());
}else{
DIV dc("tabs"); dc.flexfixe().w(side).align("stretch").bg("white").print();
htmlprintf ("\n",href.c_str());
DIV ddc; ddc.w(100).h(100).bg("lightgray").print();
ddc.end();
htmlout ("\n");
}
}
}
}
if (c.changed || glocal.changed) tlmpweb_savetabs(alltabs);
}
void webtabs(_F_webtabs &c,
const char *name,
map &tabs,
unsigned size[5])
{
webtabs (c,name,tabs,true,size);
}
void webtabs(_F_webtabs &c,
const char *name,
const vector &starttabs,
map &alltabs,
unsigned size[5])
{
WEBTAB_CTRL &ctrl = alltabs[name];
vector &tabs = ctrl.tabs;
for (auto &s:starttabs){
bool found = false;
string tmp;
const char *tab = s.c_str();
const char *title = "";
const char *pt = strchr(tab,'~');
if (pt != NULL){
title = pt + 1;
tmp = string(tab,pt-tab);
tab = tmp.c_str();
}
for (auto &w:tabs){
if (strcmp(tab,w.tab.c_str())==0){
w.title = title;
w.locked = true;
found = true;
break;
}
}
if (!found){
WEBTAB w(WEBTAB_TYPE1,tab,title,"");
w.locked = true;
tabs.push_back(w);
}
}
webtabs (c,name,alltabs,true,size);
}