/* #Specification: DIALOG / FIELD_CLIST / introduction clist == Column list: A list of record presented in columns. The FIELD_CLIST type was created 6 years after the rest of this project. The various objects such as DIALOG_RECORDS had for a long time a simple way to deal with record list. They were simple to use. Unfortunatly, they could not scale to handle more complex dialogs. For example, dialog with several lists, field and buttons. So we created the FIELD_CLIST, which is a multi-line field operating under control of a normal DIALOG object. At this point, the DIALOG_RECORDS are still built on the same FIELD_MENU objects. Note that the GUI front-end had a Clist object for a long time. So both FIELD_CLIST and DIALOG_RECORDS maps to the same GUI component. */ #include #include #include #include #include #include "internal.h" #include "dialog.h" #include #include /* Like strcmp, but handls NULL pointer as well */ static int clist_safecmp(const char *s1, const char *s2) { int ret = 0; if (s1 != NULL && s2 != NULL){ ret = strcmp(s1,s2); }else if (s1 != NULL){ ret = 1; }else if (s2 != NULL){ ret = -1; } return ret; } class CLIST_ITEM: public ARRAY_OBJ{ public: int id; char *s; char *dcs; /*~PROTOBEG~ CLIST_ITEM */ public: CLIST_ITEM (int _id, const char *_s, const char *_dcs); const char *get (void)const; void setfrom (const char *_s, const char *_dcs); ~CLIST_ITEM (void); /*~PROTOEND~ CLIST_ITEM */ }; PUBLIC CLIST_ITEM::CLIST_ITEM(int _id, const char *_s, const char *_dcs) { id = _id; s = NULL; dcs = NULL; setfrom (_s,_dcs); } PUBLIC void CLIST_ITEM::setfrom (const char *_s, const char *_dcs) { free (s); free (dcs); s = strdup(_s); dcs = NULL; if (_dcs != NULL) dcs = strdup(_dcs); } PUBLIC const char *CLIST_ITEM::get () const { return s; } PUBLIC CLIST_ITEM::~CLIST_ITEM() { free (s); free (dcs); } class CLIST_ITEMS: public ARRAY{ /*~PROTOBEG~ CLIST_ITEMS */ public: CLIST_ITEM *getitem (int no)const; /*~PROTOEND~ CLIST_ITEMS */ }; PUBLIC CLIST_ITEM *CLIST_ITEMS::getitem (int no) const { return (CLIST_ITEM*)ARRAY::getitem(no); } class FIELD_CLIST_PRIVATE{ public: SSTRING header; CLIST_ITEMS items; int nbvisible; // How many lines to display at once DIALOG *dia; int *sel; // Will contain the line selected int nolist; // Field number in this dialog of the CLIST SSTRING relpath; // path of the sub-forms holding that list int clicked_head; // Which header button was clicked or -1 bool mayclickhead; char *hsign; // Small icons on columns title (generally // to express sorting char *hdispo; // Horizontal disposition of the columns char *dcs; // Default drawing context for the clist char *nextdcs; // Drawing context to use for the next records // May be NULL int idalloc; // Counter to allocate new id // because we are allowed to delete items in the // middle of the list and the GUI front-end // remember the old ids. FIELD_CLIST_PRIVATE(int _nbvisible, DIALOG *_dia, int &_sel){ nbvisible = _nbvisible; dia = _dia; sel = &_sel; nolist = 0; mayclickhead = false; clicked_head = -1; hsign = NULL; hdispo = NULL; dcs = NULL; nextdcs = NULL; idalloc = 0; } ~FIELD_CLIST_PRIVATE(){ free (hsign); free (hdispo); free (dcs); } }; PUBLIC FIELD_CLIST::FIELD_CLIST( const char *_prompt, int nbvisible, DIALOG *dia, PRIVATE_MESSAGE &_msg, int &sel) : FIELD (_prompt) { priv = new FIELD_CLIST_PRIVATE(nbvisible,dia,sel); msg = &_msg; } PUBLIC FIELD_CLIST::~FIELD_CLIST() { delete priv; } /* Record the heading of the table. Columns are separated by tabs */ PUBLIC void FIELD_CLIST::setheader(const char *s) { priv->header.setfrom (s); update_head(); } PRIVATE void FIELD_CLIST::update_head() { SSTRING tmp; const char *dianame = priv->dia->setguiname(tmp); if (dianame != NULL){ char prefix[1000],tmp1[1000]; sprintf (prefix,"%s.c%d \"\"",formatpath(tmp1,dianame),priv->nolist); sendhead (P_Setval,prefix); } } /* Add a new record */ PUBLIC void FIELD_CLIST::addrecord (const char *s) { priv->items.add (new CLIST_ITEM(priv->idalloc++,s,priv->nextdcs)); } PUBLIC void FIELD_CLIST::addrecordf (const char *s, ...) { va_list list; va_start (list,s); char tmp[10000]; vsnprintf (tmp,sizeof(tmp)-1,s,list); va_end (list); addrecord (tmp); } PUBLIC void FIELD_CLIST::vaddrecordf (const char *s, va_list list) { char tmp[10000]; vsnprintf (tmp,sizeof(tmp)-1,s,list); addrecord (tmp); } /* Insert a new record at a specific position */ PUBLIC void FIELD_CLIST::insrecord (int pos, const char *s) { priv->items.insert (pos,new CLIST_ITEM(priv->idalloc++,s,priv->nextdcs)); } /* Set the drawing context for the next records (defined using setrecord...) */ PUBLIC void FIELD_CLIST::setnextdcs(const char *dcs) { free (priv->nextdcs); priv->nextdcs = NULL; if (dcs != NULL){ priv->nextdcs = strdup(dcs); } } /* Replace the value of a record. If the record does not exist the value is added to the list. */ PUBLIC void FIELD_CLIST::setrecord (int no, const char *s) { CLIST_ITEM *item = priv->items.getitem(no); bool differ = false; bool setval = false; int id=0; if (item == NULL){ id = priv->idalloc; addrecord (s); differ = true; }else if (strcmp(item->s,s)!=0 || clist_safecmp(priv->nextdcs,item->dcs)!=0){ item->setfrom (s,priv->nextdcs); id = item->id; differ = true; setval = true; } if (differ){ SSTRING tmp; const char *dianame = priv->dia->setguiname(tmp); if (dianame != NULL){ char line[1000],word[1000]; ftitle_splitline (s,line,word); char tmp[1000],tmp1[1000]; int command = setval? P_Setval : P_Clist_item; char tmpdcs[1000]; if (priv->nextdcs != NULL){ snprintf (tmpdcs,sizeof(tmpdcs)-1," $dcs=%s",priv->nextdcs); }else{ tmpdcs[0] = '\0'; } diagui_sendcmd (command,"%s.c%d L%d %s %s%s\n" ,formatpath(tmp1,dianame) ,priv->nolist,id ,line,diagui_quote(word,tmp),tmpdcs); }else{ priv->dia->reset_guidone(); } } } PUBLIC void FIELD_CLIST::setrecordf (int pos, const char *s, ...) { va_list list; va_start (list,s); char tmp[10000]; vsnprintf (tmp,sizeof(tmp)-1,s,list); va_end (list); setrecord (pos,tmp); } PUBLIC void FIELD_CLIST::vsetrecordf (int pos, const char *s, va_list list) { char tmp[10000]; vsnprintf (tmp,sizeof(tmp)-1,s,list); setrecord (pos,tmp); } /* Return the number of record in the list */ PUBLIC int FIELD_CLIST::getnb () const { return priv->items.getnb(); } /* Return one item of the list */ PUBLIC const char *FIELD_CLIST::getline (int no) const { const char *ret = NULL; CLIST_ITEM *s = priv->items.getitem(no); if (s != NULL) ret = s->get(); return ret; } /* Remove all items after item N */ PUBLIC void FIELD_CLIST::remove_last (int n) { for (int i=getnb()-1; i>=n; i--) remove(i); } /* Remove one item from the list */ PUBLIC void FIELD_CLIST::remove (int n) { CLIST_ITEM *item = priv->items.getitem(n); if (item != NULL){ SSTRING tmp; const char *dianame = priv->dia->setguiname(tmp); if (dianame != NULL && diajava_clistdel){ char tmp1[1000]; diagui_sendcmd (P_Setval,"%s.c%d L%d $del=1\n" ,formatpath(tmp1,dianame) ,priv->nolist,item->id); }else{ priv->dia->reset_guidone(); } priv->items.remove_del(n); } } /* Send either a Clist definition or a re-definition using Setval */ PRIVATE void FIELD_CLIST::sendhead(int guicmd, const char *prefix) { char line[1000],word[1000]; int nbhead = ftitle_splitline (priv->header.get(),line,word); char hid[10]; if (priv->mayclickhead){ sprintf (hid," hid=B%d",priv->nolist); }else{ hid[0] = '\0'; } char hsign[100]; if (priv->hsign != NULL){ snprintf (hsign,sizeof(hsign)-1," hsign=%s",priv->hsign); }else{ hsign[0] = '\0'; } char dcs[1000]; if (priv->dcs != NULL){ snprintf (dcs,sizeof(dcs)-1," dcs=%s",priv->dcs); }else{ dcs[0] = '\0'; } char hdisp[100]; if (priv->hdispo != NULL && priv->hdispo[0] != '\0'){ snprintf (hdisp,sizeof(hdisp)-1," hdisp=%s",priv->hdispo); }else{ hdisp[0] = '\0'; } char tmp[1000]; diagui_sendcmd (guicmd,"%s %d %s %s $vsize=%d%s%s%s%s\n" ,prefix,nbhead ,line,diagui_quote(word,tmp),priv->nbvisible ,hdisp,hid,hsign,dcs); } PRIVATE void FIELD_CLIST::gui_draw (int nof, SSTRINGS &tb) { guisendprompt(); priv->nolist = nof; const char *ctl = "%s"; priv->relpath.setfrom (""); for (int i=0; irelpath.appendf (ctl,tb.getitem(i)->get()); ctl = ".%s"; } char prefix[10]; sprintf (prefix,"c%d",nof); sendhead(P_Clist,prefix); int nb = priv->items.getnb(); for (int i=0; iitems.getitem(i); char line[1000],word[1000]; ftitle_splitline (item->get(),line,word); char tmpdcs[1000]; if (item->dcs != NULL){ snprintf (tmpdcs,sizeof(tmpdcs)-1," $dcs=%s",item->dcs); }else{ tmpdcs[0] = '\0'; } char tmp[1000]; diagui_sendcmd (P_Clist_item,"\"\" L%d %s %s%s\n",item->id ,line,diagui_quote(word,tmp),tmpdcs); } diagui_sendcmd (P_End,"\n"); } PRIVATE void FIELD_CLIST::drawtxt (WINDOW *, int, int, int) { } PRIVATE MENU_STATUS FIELD_CLIST::dokey(WINDOW *, int, FIELD_MSG &, bool &) { return MENU_NULL; } PUBLIC void FIELD_CLIST::save() { } PUBLIC void FIELD_CLIST::restore() { } PUBLIC void FIELD_CLIST::reload(const char *, int) { } PUBLIC void FIELD_CLIST::html_draw(int) { } PUBLIC void FIELD_CLIST::xul_draw(int, SSTRINGS &) { } PUBLIC MENU_STATUS FIELD_CLIST::gui_get(int no, const char *field_id, const char *actionid) { MENU_STATUS ret = MENU_NULL; if (field_id[0] == 'c' && atoi(field_id+1)==no){ int sel = atoi(actionid+1); priv->clicked_head = -1; if (actionid[0] == 'L'){ for (int i=0; iitems.getnb(); i++){ CLIST_ITEM *item = priv->items.getitem(i); if (item->id == sel){ *priv->sel = i; break; } } }else{ const char *head = diajava_getextrareport(); if (head != NULL){ priv->clicked_head = atoi(head); } } ret = MENU_MESSAGE; } return ret; } PUBLIC char FIELD_CLIST::getidprefix() { return 'c'; } PUBLIC int FIELD_CLIST::html_validate(int) { return 0; } PUBLIC const char * FIELD_CLIST::get_registry_value() { return NULL; } PUBLIC void FIELD_CLIST::set_registry_value(const char *) { } /* Set the cursor on a specific line of the clist. If focus is true, the clist will have keyboard focus as well. */ PUBLIC void FIELD_CLIST::setcursor (int pos, bool focus) { CLIST_ITEM *item = priv->items.getitem(pos); if (item != NULL && dialog_mode == DIALOG_GUI){ SSTRING tmp; const char *dianame = priv->dia->setguiname(tmp); if (dianame != NULL){ char tmp1[1000]; diagui_sendcmd (P_Curfield,"%s.c%d L%d $focus=%d\n" ,formatpath(tmp1,dianame) ,priv->nolist,item->id ,focus ? 1 : 0); } } } /* Tell this CLIST has clickable header. Generally used to change the sort order (click on a column). This must be called before the first DIALOG::edit(). */ PUBLIC void FIELD_CLIST::mayclickhead () { priv->mayclickhead = true; } /* Return the number of the clicked column or -1. FIELD_CLIST::mayclickhead() must have been called to enable column operations. */ PUBLIC int FIELD_CLIST::whichcolumn () { return priv->clicked_head; } /* Set the small column icons definition (one icon per letter) - means no icon. u means UP. d means Down. Other may be defined... */ PUBLIC void FIELD_CLIST::sethsign (const char *hsign) { if (clist_safecmp(priv->hsign,hsign)!=0){ free (priv->hsign); priv->hsign = strdup(hsign); update_head(); } } PUBLIC void FIELD_CLIST::sethsign (int column, const char letter) { char line[1000],word[1000]; int nbhead = ftitle_splitline (priv->header.get(),line,word); if (column >= nbhead){ fprintf (stderr,"FIELD_CLIST::sethsign column to large %d > %d\n" ,column,nbhead); }else{ char tmp[nbhead+1]; memset (tmp,'-',nbhead); tmp[nbhead] = '\0'; tmp[column] = letter; sethsign (tmp); } } /* Set the horizontal disposition of the columns (l,c,r). One letter per columns, l for left is the default for the unspecified columns. */ PUBLIC void FIELD_CLIST::sethdispo (const char *hdispo) { if (hdispo == NULL) hdispo = ""; if (clist_safecmp(priv->hdispo,hdispo)!=0){ free (priv->hdispo); priv->hdispo = strdup(hdispo); update_head(); } } /* Set the drawing context associated with each column. They are separated by commas. */ PUBLIC void FIELD_CLIST::setdcs (const char *dcs) { if (clist_safecmp(priv->dcs,dcs)!=0){ free (priv->dcs); priv->dcs = strdup(dcs); update_head(); } } PUBLIC FIELD_CLIST *DIALOG::newf_clist ( const char *prompt, int nbvisible, PRIVATE_MESSAGE &msg, int &sel) { FIELD_CLIST *ret = new FIELD_CLIST (prompt,nbvisible,this,msg,sel); add (ret); return ret; }