#include #include #include #include #include #include #include #include "internal.h" #include "dialog.h" #include "../diajava/proto.h" #include "../diajava/protorev.h" #include "../paths.h" #include "dialog.m" static DIAJAVA *java = NULL; static void diagui_end() { delete java; java = NULL; } static DIAGUI_MODE diagui_mode=DIAGUI_AUTO; static int diagui_handlein=-1; static int diagui_handleout=-1; static const char *diagui_lines; /* Disable the GUI mode for the current session */ void diagui_setmode(DIAGUI_MODE mode) { diagui_mode = mode; } /* Set the socket handle used to talk with the GUI front end */ void diagui_sethandle(int handlein, int handleout, const char *lines) { diagui_handlein = handlein; diagui_handleout = handleout; diagui_lines = lines; } /* Initialise communication with the user interface server */ int diagui_init () { int ret = -1; atexit (diagui_end); bool guiok = true; if (diagui_handlein == -1){ if (diagui_mode == DIAGUI_AUTO){ guiok = linuxconf_getguimode(); }else if (diagui_mode == DIAGUI_NOGUI){ guiok = false; } } uithread_init (20); if (diagui_handlein != -1){ java = new DIAJAVA(diagui_handleout,diagui_handlein,diagui_lines); }else{ java = new DIAJAVA(guiok); } if (java->is_ok()){ char tmp[1000]; diagui_send ("Version %d %s\n",PROTOGUI_REV ,diagui_quote(MSG_U(E_GUIVER,"Invalid GUI protocol version") ,tmp)); char dianame[200],actionid[100],menubarid[100]; int menu,but; POPENWAITS tbo; java->wait (tbo,dianame,actionid,menubarid,menu,but); sleep(2); if (strcmp(actionid,"ncurses")!=0){ ret = 0; }else{ delete java; java = NULL; } }else{ delete java; java = NULL; } return ret; } static int diagui_lastbut,diagui_lastmenu; static char actionid[100]; static char menubarid[100]; static char diapath[200]; static const char *fieldid=""; class DIAGUI_CTXS: public SSTRING_KEYS{ public: void add (const char *dianame) { char idstr[20]; sprintf (idstr,"%d",uithread_id); SSTRING_KEYS::add (dianame,idstr); } void remove_del (const char *dianame) { SSTRING_KEYS::remove_del (getobj(dianame)); } }; static DIAGUI_CTXS valids; /* Return the id of the last button pressed (or entry selection in a tree menu) */ EXPORT const char *diagui_getlast_actionid() { return actionid; } class DIAGUI_MESSAGE: public ARRAY_OBJ{ public: SSTRING text; PRIVATE_MESSAGE *priv; SSTRING dianame; DIAGUI_MESSAGE( const char *_dianame, const char *s, PRIVATE_MESSAGE *p){ text.setfrom (s); priv = p; dianame.setfrom(_dianame); } DIAGUI_MESSAGE(){ priv = NULL; } DIAGUI_MESSAGE &operator =(DIAGUI_MESSAGE &m){ priv = m.priv; text.setfrom (m.text); dianame = m.dianame; return *this; } bool match (const char *_dianame,const char *s){ return text.cmp(s)==0 && dianame.cmp(_dianame)==0; } bool match (const char *_dianame, PRIVATE_MESSAGE *p){ return priv == p && dianame.cmp(_dianame)==0; } void reset(){ priv = NULL; text.setfrom (""); dianame.setempty(); } bool is_empty(){ return text.is_empty() && priv == NULL; } }; class DIAGUI_MESSAGES: public ARRAY{ public: DIAGUI_MESSAGE *getitem(int no) const{ return (DIAGUI_MESSAGE*)ARRAY::getitem(no); } void add (const char *dianame,const char *s){ ARRAY::add(new DIAGUI_MESSAGE(dianame,s,NULL)); } void add (const char *dianame, PRIVATE_MESSAGE *p){ ARRAY::add(new DIAGUI_MESSAGE(dianame,NULL,p)); } }; static DIAGUI_MESSAGES messages; static DIAGUI_MESSAGE lastmsg; class POPENWAITID: public POPENWAIT{ public: int threadid; bool mustreturn; POPENWAITID (POPENFD &_po, int _timeout, int _threadid) : POPENWAIT (_po,_timeout){ threadid = _threadid; mustreturn = false; } POPENWAITID (int _maxfd, fd_set *_fds, int _timeout, int _threadid) : POPENWAIT (_fds, _maxfd, _timeout){ threadid = _threadid; mustreturn = false; } }; static POPENWAITS tbpopen; /* Forget all recorded messages */ void diagui_resetmsg() { lastmsg.reset (); } static bool something_sent=false; static int diagui_wait() { int ret = -1; // Can't return -1 uithread_check(); diagui_resetmsg(); diapath[0] = '\0'; actionid[0] = '\0'; menubarid[0] = '\0'; diagui_lastbut = -1; fieldid = ""; while (messages.getnb() > 0){ DIAGUI_MESSAGE *msg = messages.getitem(0); const char *dianame = msg->dianame.get(); SSTRING_KEY *ct = valids.getobj(dianame); if (ct != NULL){ ret = atoi(ct->getobjval()); lastmsg = *msg; messages.remove_del (0); break; } messages.remove_del (0); } if (ret == -1){ for (int i=0; imustreturn){ p->mustreturn = false; ret = p->threadid; break; } } } if (ret == -1){ static bool wakeupfront = false; if (wakeupfront || something_sent){ wakeupfront = false; if (diajava_alive) diagui_sendcmd (P_Alive,"\n"); something_sent = false; } while (ret == -1){ multi_setlistening(); int retsel = java->wait(tbpopen,diapath,actionid,menubarid ,diagui_lastmenu,diagui_lastbut); if (retsel == 0){ // fprintf (stderr,"Must return\n"); for (int i=0; imustreturn = true; } } for (int i=0; iretcode > 0 || p->mustreturn){ p->mustreturn = false; ret = p->threadid; break; } } if (ret == -1){ // fprintf (stderr,"diagui_wait %d %d %s\n",diagui_lastmenu,diagui_lastbut,diapath); // Check if the dialog is listening char *pt = strrchr (diapath,'.'); if (pt != NULL){ fieldid = pt + 1; } pt = strchr (diapath,'.'); if (pt != NULL) *pt = '\0'; SSTRING_KEY *ct = valids.getobj(diapath); if (ct != NULL){ ret = atoi(ct->getobjval()); if (pt != NULL) *pt = '.'; // Something was received from the front-end // We will have to tell it when we are back listening wakeupfront = true; } } } } return ret; } EXPORT const char *diagui_quote (const char *s, char tmp[1000]) { const char *ret = s; bool doquote = s[0] == '\0' || s[0] == '$'; if (!doquote){ const char *pt = s; while (*pt != '\0'){ if (*pt <= ' ' || *pt == '"'){ doquote = true; break; } pt++; } } if (doquote){ ret = tmp; char *pt = tmp; *pt++ = '"'; while (*s != '\0' && (pt-tmp) < 997){ if (*s == '"' || *s == '\\'){ *pt++ = '\\'; *pt++ = *s; }else{ *pt++ = *s; } s++; } *pt++ = '"'; *pt++ = '\0'; } return ret; } /* Send a command to the user interface server */ void diagui_send (const char *ctl, ...) { if (java != NULL){ char buf[1000]; va_list list; va_start (list,ctl); vsnprintf (buf,sizeof(buf)-1,ctl,list); java->send ("%s",buf); //fprintf (stderr,"guisend: %s",buf); va_end (list); } } /* Send a command to the user interface server */ EXPORT void diagui_sendcmd (int cmd, const char *ctl, ...) { if (java != NULL){ something_sent = true; char buf[1000]; va_list list; va_start (list,ctl); vsprintf (buf,ctl,list); java->sendcmd (cmd,"%s",buf); //fprintf (stderr,"guisend: %s",buf); va_end (list); } } /* Flush the command sent to the front-end */ EXPORT void diagui_flush () { if (java != NULL) java->flush(); } void diagui_send_Label(const char *str) { /* #Specification: GUI layout / no label for a field All field definitions are done using function like DIALOG::newf_str(). The first argument is the "prompt" or field title. Sometime, we do not want any field title and do not want any space left in the layout. By sending a NULL, we are telling the layout engine to skip the prompt completly. Now, to avoid little problems here and there in the dialog project, a NULL prompt is recorded as the string "\n". */ if (str[0] != '\n'){ char tmp[1000]; diagui_sendcmd (P_Label,"%s\n",diagui_quote(str,tmp)); } } static SSTRINGS iconpaths; /* Record one path to find xpm icons. The function may be called several time. */ EXPORT void diagui_seticonpath (const char *path) { iconpaths.add (new SSTRING(path)); } /* Send an Icon_xpm command to the user interface server */ static int sendxpm (const char *name, char *name_sent, bool mini) { int ret = -1; static SSTRINGS sofar; // List of icons already transmitted if (mini) { sprintf(name_sent, "mini-%s", name); } else { strcpy(name_sent, name); } if (sofar.lookup(name_sent)!=-1){ ret = 0; }else{ if (iconpaths.getnb()==0) diagui_seticonpath(USR_LIB_LINUXCONF "/images"); FILE *fin = NULL; if (name[0] == '/'){ if (diajava_jpeg && (stristr(name,".jpg")!=NULL||stristr(name,".jpeg")!=NULL)){ fin = fopen (name,"r"); if (fin != NULL){ unsigned char buf[400]; int n; while ((n=fread(buf,1,sizeof(buf),fin))>0){ char tmp[2*sizeof(buf)+1]; // Turn to hex char *pt = tmp; unsigned char *ptbuf = buf; for (int i=0; i>4]; *pt++ = lk[car&0x0f]; } *pt = '\0'; diagui_sendcmd (P_Str,"%s\n",tmp); } fclose (fin); fin = NULL; diagui_sendcmd (P_Xfer_jpg,"%s\n",name_sent); sofar.add (new SSTRING(name_sent)); ret = 0; } }else if (strstr(name,".xpm")==NULL){ static const char *convert_path = NULL; if (convert_path == NULL){ if (file_exist ("/usr/X11R6/bin/convert")){ convert_path = "/usr/X11R6/bin/convert"; }else if (file_exist ("/usr/bin/convert")){ convert_path = "/usr/bin/convert"; }else{ fprintf (stderr,"No convert utility found, can't convert bitmaps\n"); convert_path = "/usr/lib/linuxconf/lib/shownoconvert.sh"; } } SSTRING tmp; tmp.setfromf ("%s xpm:-",name); POPEN pop (convert_path,tmp.get()); if (pop.isok()){ while (pop.wait(10)>0){ char line[800]; while (pop.readout(line,sizeof(line)-1)!=-1){ strip_end (line); char tmp[1000]; diagui_sendcmd (P_Str,"%s\n",diagui_quote(line,tmp)); } } diagui_sendcmd (P_Xfer_xpm,"%s\n",name_sent); sofar.add (new SSTRING(name_sent)); } }else{ fin = fopen (name,"r"); } }else{ for (int i=0; i< iconpaths.getnb() && fin == NULL; i++){ char path[PATH_MAX]; sprintf (path,"%s/%s.xpm",iconpaths.getitem(i)->get() ,name_sent); fin = fopen (path,"r"); } if (fin == NULL){ if (mini) { strcpy (name_sent,"mini-missing_icon"); } else { strcpy (name_sent,"missing_icon"); } if (sofar.lookup(name_sent)!=-1){ ret = 0; }else{ char path[PATH_MAX]; sprintf (path,"%s/images/%s.xpm",USR_LIB_LINUXCONF,name_sent); fin = fopen (path,"r"); } } } if (fin != NULL){ char buf[1000]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ int last = strlen (buf)-1; if (last >=0 && buf[last] == '\n') buf[last] = '\0'; char tmp[1000]; diagui_sendcmd (P_Str,"%s\n",diagui_quote(buf,tmp)); } fclose (fin); diagui_sendcmd (P_Xfer_xpm,"%s\n",name_sent); sofar.add (new SSTRING(name_sent)); ret = 0; } } return ret; } EXPORT int diagui_sendxpm ( const char *name, // Name of the icon to send char *name_sent) // Name selected if this one was missing { return sendxpm(name, name_sent, false); } EXPORT int diagui_sendminixpm ( const char *name, // Name of the icon to send char *name_sent) // Name selected if this one was missing { return sendxpm(name, name_sent, true); } /* Get the dialog ID already used to setup the FORM object in the front-end Return NULL if the front-end do not support the setval primitive or if the FORM has not been created yet. Return tmp if ok. */ PUBLIC const char *DIALOG::setguiname(SSTRING &tmp) { const char *dianame = NULL; tmp.clear(); if (internal->guidone_once && internal->guidone && diajava_setval){ if (!internal->context.is_empty()){ tmp.setfromf ("%s.",internal->context.get()); } tmp.appendf ("main-%d-%d",internal->thread_id,internal->gui_id); dianame = tmp.get(); } return dianame; } /* Return only the base name of the dialog, without the GUI context. This is needed when you want to retrieve dump info or want to wait yourself for the dialog (diagui_wait()). */ PUBLIC const char *DIALOG::setguibasename(SSTRING &tmp) { tmp.setfromf ("main-%d-%d",internal->thread_id,internal->gui_id); return tmp.get(); } static SSTRING default_context; // Default context for DIALOGs /* Record the default context in which dialog may be inserted. A context is a path inside an existing dialog. */ EXPORT void diagui_setdefaultctx (const char *s) { default_context.setfrom (s); } /* Ask the front-end to delete (forget) this DIALOG */ PRIVATE void DIALOG::guidelete() { if (internal->guidone){ internal->guidone = false; if (internal->context.is_empty()){ diagui_sendcmd (P_Delete,"main-%d-%d\n" ,internal->thread_id ,internal->gui_id); }else{ diagui_sendcmd (P_Delete,"%s.main-%d-%d\n" ,internal->context.get() ,internal->thread_id,internal->gui_id); } } } /* Hide the DIALOG from the screen. Only useful in graphic mode. No effect for other mode. */ PUBLIC void DIALOG::hide() { guidelete(); } PRIVATE void DIALOG::sendintro() { if (internal->guidone){ char *intropath; if (internal->intro.cmp(internal->last_intro) == 0){ return; } if (internal->context.is_empty()){ asprintf(&intropath, "main-%d-%d.intro" ,internal->thread_id,internal->gui_id); }else{ asprintf(&intropath, "%s.main-%d-%d.intro" ,internal->context.get() ,internal->thread_id,internal->gui_id); } diagui_sendcmd (P_Deletechild,"%s\n", intropath); diagui_sendcmd (P_Setcontext,"%s\n", intropath); free(intropath); }else{ diagui_sendcmd (P_Form,"intro $vexpand=0\n"); } internal->last_intro.setfrom(internal->intro); if (internal->icon.is_filled()){ char name_sent[PATH_MAX]; diagui_sendxpm (internal->icon.get(),name_sent); diagui_sendcmd (P_Icon_xpm,"%s\n",name_sent); diagui_sendcmd (P_Dispolast,"l 1 c 1\n"); diagui_sendcmd (P_Form,"subintro\n"); } // Extract the lines and convert the tabs const char *para = internal->intro.get(); while (*para != '\0'){ char tmp[100]; char *pt = tmp; int pos = 0; while (*para != '\0' && *para != '\n'){ char car = *para++; if (car == '\t'){ if ((pos & 7)==0){ *pt++ = ' '; pos++; } while ((pos & 7) != 0){ *pt++ = ' '; pos++; } }else{ *pt++ = car; pos++; } } *pt = '\0'; if (*para == '\n') para++; diagui_send_Label (tmp); diagui_sendcmd (P_Newline,"\n"); } diagui_sendcmd (P_End,"\n"); if (internal->icon.is_filled()){ diagui_sendcmd (P_End,"\n"); } } PRIVATE void DIALOG::showgui(int &nof, int but_options) { extern int uithread_id; if (!internal->guidone){ if (internal->guidone_once && !diajava_reconfdia){ guidelete(); // We allocate a new id as this seem to confuse gnome-linuxconf internal->gui_id = multi_alloc_gui_id(); internal->thread_id = uithread_id; } internal->guidone_once = true; char tmp[1000]; DIALOG_TYPE diatype = internal->diatype; #if 0 if (getnb()==1 && (but_options & MENUBUT_ACCEPT)!=0 && internal->diatype == DIATYPE_STD){ diatype = DIATYPE_POPUP; } #endif static char *tbtype[]={"std","error","notice","popup"}; if (diatype == DIATYPE_STD && !internal->context_wasset){ setcontext (default_context.get()); } bool context_doend = false; if (!internal->context.is_empty()){ diagui_sendcmd (P_Setcontext,"%s\n",internal->context.get()); context_doend = true; } diagui_sendcmd (P_MainForm,"main-%d-%d %s %s\n" ,internal->thread_id ,internal->gui_id,diagui_quote(internal->title.get(),tmp) ,tbtype[diatype]); if (diatype == DIATYPE_POPUP){ diagui_sendcmd (P_Enteraction,"B%d\n",BUTSPC_ENTER); }else{ //diagui_sendcmd (P_Sidetitle,"%s\n",diagui_quote(internal->sidetitle.get(),tmp)); } bool is_intro = !internal->intro.is_empty(); if (is_intro){ sendintro(); diagui_sendcmd (P_Dispolast,"c 1 t 1\n"); diagui_sendcmd (P_Newline,"\n"); } int lastf = getnb(); if (lastf > 0){ diagui_sendcmd (is_intro ? P_Group : P_Form,"panel%s%s\n" ,internal->guiparms.is_empty() ? "" : " $" ,internal->guiparms.get()); SSTRINGS subs; subs.add (new SSTRING("panel")); /* #Specification: GUI / layout / first section It is possible to create a dialog with a first visible section followed by a notebook containing "less" visible sections (options). This is done by putting DIALOG::newf_title() calls after the first section. We can also do a normal notebook dialog where all sections (pages) are selectable with the page tab. This is done by starting the dialog with DIALOG::newf_title (Each page starts with this). For the first case (When the dialog does not start with the newf_title() function), linuxconf puts the first section inside of a Form centered horizontally in the dialog. */ int first_form_end = -1; bool autonewline = internal->autonewline; bool managed_newline = false; for (int i=0; igetnotepadlevel() > 0 && first_form_end == -1){ first_form_end = i; }else if (f->is_passthrough()){ autonewline = false; }else if (f->getflags(flags)){ if (flags & (DIAFLAGS_AUTONEWLINE|DIAFLAGS_NOAUTONEWLINE)){ managed_newline = true; } } } if (managed_newline) autonewline = internal->autonewline; if (first_form_end > 0){ diagui_sendcmd (P_Form,"first\n"); subs.add (new SSTRING("first")); } for (int i=0; igetflags(flags)){ if (flags & DIAFLAGS_AUTONEWLINE){ autonewline = true; }else if (flags & DIAFLAGS_NOAUTONEWLINE){ autonewline = false; } }else{ f->gui_draw (i,subs); f->draw_helpdia(NULL,i); { // Record the relative path of the widget char path[1000]; int lenpath = 0; path[0] = '\0'; for (int j=0; jget()); } f->set_guipath(path); } if (autonewline) diagui_sendcmd (P_Newline,"\n"); } } for (int i=subs.getnb(); i > 0; i--){ diagui_sendcmd (P_End,"\n"); } // diagui_sendcmd (P_End,"\n"); if (!internal->button_on_side){ diagui_sendcmd (P_Dispolast,"c 1 t 1\n"); diagui_sendcmd (P_Newline,"\n"); } } internal->buttons->gui_draw (internal->button_on_side); diagui_sendcmd (P_End,"\n"); if (context_doend){ diagui_sendcmd (P_End,"\n"); } internal->guidone = true; } else { sendintro(); } } PRIVATE MENU_STATUS DIALOG::editgui_thread(int &nof, int but_options) { bool nof_changed = true; if (nof == -1){ nof_changed = false; nof = internal->last_nof; } // Select the first editable field while (nof < getnb()){ FIELD *f = getitem(nof); if (f != NULL && f->readonly && !f->may_select){ nof++; }else{ break; } } FIELD *f = NULL; if (nof != getnb()){ f = getitem(nof); } if (f != NULL && nof_changed) { // Only send a Curfield command if nof has changed SSTRING guipath; if (f->guipath.getlen() > 0){ guipath.setfrom("."); guipath.append(f->guipath.get()); } SSTRING ctx; if (!internal->context.is_empty()){ ctx.setfromf ("%s.",internal->context.get()); } diagui_sendcmd (P_Curfield,"%smain-%d-%d%s %c%d\n",ctx.get() ,internal->thread_id ,internal->gui_id,guipath.get(),f->getidprefix(),nof); } // Reset nof so we can detect if it has changed nof = -1; MENU_STATUS ret = MENU_NULL; char dianame[20]; // Same name we are building above sprintf (dianame,"main-%d-%d",internal->thread_id,internal->gui_id); valids.add (dianame); while (1){ uithread_sync (diagui_wait); if (lastmsg.is_empty()){ break; }else if (internal->waitmsg.lookup(lastmsg.text.get()) != -1 || internal->waitprivmsg.lookup (lastmsg.priv) != -1){ ret = MENU_MESSAGE; break; } } valids.remove_del (dianame); internal->gui_getdone = false; if (ret == MENU_NULL){ int n = getnb(); if (diagui_lastbut != -1){ for (int i=0; igui_get (i,fieldid,actionid); if (st != MENU_NULL){ ret = st; lastmsg.priv = f->msg; } } internal->gui_getdone = true; if (ret == MENU_NULL){ if (diagui_lastbut >= 200 && diagui_lastbut < 200+n){ int no = diagui_lastbut-200; FIELD *f = getitem(no); if (f->msg != NULL){ ret = MENU_MESSAGE; lastmsg.priv = f->msg; nof = no; } }else{ ret = internal->buttons->bid2status(diagui_lastbut); } } }else if (diagui_lastmenu != -1){ if (actionid[0] == 'M'){ nof = diagui_lastmenu; ret = MENU_OK; }else{ // Ok, we check the actionid to find out which clist was // clicked for (int i=0; igui_get (i,fieldid,actionid); if (st == MENU_MESSAGE){ ret = st; lastmsg.priv = f->msg; } } internal->gui_getdone = true; } } } if (nof != -1){ internal->last_nof = nof; } return ret; } /* Wait for an event for this uithread. This function is used by dialogs which completly bypass the DIALOG object and build their GUI by talking directly to the GUI front-end (using diagui_sendcmd). Those dialogs are really "on their own". So far only the treemenu module is using that as the tree widget is not integrated in the DIALOG object. This function returns 0 when something was selected in the dialog (on element of the treemenu for example). In that case, action contains the selected item. The function returns > 0 when a button is selected. The value is the ID of the button as passed using diagui_sendcmd(). */ EXPORT int diagui_sync ( const char *dianame, // Base name of the dialog SSTRING &path, // path of the selected component // (button, treemenu) SSTRING &action, // Action associated with the component SSTRING &menubar) // menubar selection { int ret = -1; valids.add (dianame); uithread_sync (diagui_wait); valids.remove_del (dianame); path.setfrom (diapath); action.setfrom (""); menubar.setfrom (""); if (diagui_lastbut != -1){ ret = diagui_lastbut; }else{ action.setfrom (actionid); menubar.setfrom (menubarid); ret = 0; } return ret; } /* Wait for an event on a POPEN connection for this uithread. */ static int diagui_sync ( POPENWAITID *w, bool message_aware) { tbpopen.add (w); char dianame[10]; // We fake a dialog so this thread will react to PRIVATE_MESSAGE // This means this function will return 0 whenever a PRIVATE_MESSAGE // is sent. sprintf (dianame,"wait-%d",uithread_id); if (message_aware) valids.add (dianame); uithread_sync (diagui_wait); valids.remove_del (dianame); int ret = w->retcode; tbpopen.remove_del (w); return ret; } EXPORT int diagui_sync ( POPENFD &po, int timeout, bool message_aware) // The caller wants to handle PRIVATE_MESSAGEs { POPENWAITID *w = new POPENWAITID (po,timeout,uithread_id); return diagui_sync (w,message_aware); } /* Wait for an event on a POPEN connection for this uithread. */ EXPORT int diagui_sync (POPENFD &po, int timeout) { return diagui_sync (po,timeout,false); } /* Perform a select system call while dispatching GUI events. The function returns if the "end" message is sent also (-2 is returned in this case). */ EXPORT int diagui_select ( int maxhandle, fd_set *readfds, int timeout, PRIVATE_MESSAGE &end) { int ret = -1; while (1){ POPENWAITID *w = new POPENWAITID (maxhandle,readfds,timeout,uithread_id); ret = diagui_sync (w,true); if (dialog_testmessage(end)){ ret = -2; break; }else if (ret >= 0){ break; } } return ret; } /* Wait for a PRIVATE_MESSAGE. Return -1 if any error. */ EXPORT int dialog_waitformessage (PRIVATE_MESSAGE &msg) { int ret = -1; if (dialog_mode == DIALOG_GUI){ char dianame[10]; sprintf (dianame,"wait-%d",uithread_id); valids.add (dianame); while (1){ uithread_sync (diagui_wait); if (dialog_testmessage(msg)) break; } valids.remove_del (dianame); ret = 0; } return ret; } /* Transmit an html help to the GUI frontend */ void diagui_sendhtmlhelp (const char *relpath) { char path[PATH_MAX]; if (html_locatefile(relpath,"",path,PATH_MAX)!=-1){ FILE *fin = fopen (path,"r"); if (fin != NULL){ char buf[500]; diagui_sendcmd (P_Html,"%s\n",path); while (fgets_strip(buf,sizeof(buf)-1,fin,NULL)!=NULL){ char tmp[1000]; diagui_sendcmd (P_Str,"%s\n",diagui_quote(buf,tmp)); } diagui_sendcmd (P_End,"\n"); } } } static void ft (void *p) { char *rpath = (char *)p; char path[PATH_MAX]; if (html_locatefile (rpath,diajava_html ? ".html" : ".help" ,path,PATH_MAX)!=-1){ if (diajava_html){ DIALOG dia; dia.settype (DIATYPE_POPUP); dia.newf_file_html (NULL,70,40,path); int nof = 0; while (1){ MENU_STATUS code = dia.edit (rpath,"",help_none,nof,MENUBUT_QUIT); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ break; } } }else{ dialog_textbox (path,path); } }else{ xconf_error (MSG_R(E_NOHELPFILE),rpath); } free (rpath); } /* Present a help screen. Function used internally and by some module handling the GUI themselves */ void diagui_showhelp (const char *relpath) { char *pt = strdup(relpath); uithread (ft,(void*)pt); } /* Present a help screen. Function used internally and by some module handling the GUI themselves */ EXPORT void diagui_showhelp (HELP_FILE &helpfile) { char rpath[PATH_MAX]; helpfile.getrpath(rpath); diagui_showhelp (rpath); } PRIVATE MENU_STATUS DIALOG::editgui( int &nof, int but_options) { MENU_STATUS ret = MENU_NULL; while (1){ ret = editgui_thread (nof,but_options); if (ret != MENU_HELP) break; internal->listening = false; internal->buttons->help (NULL); internal->listening = true; } return ret; } /* Return the current value of a field All field have an ID in the java frontend. This id is generally a letter followed by a number. */ EXPORT const char *diagui_getval (char prefix, int nof) { return diagui_getval (NULL,prefix,nof); } EXPORT const char *diagui_getval (const char *diapath, char prefix, int nof) { char id[100]; sprintf (id,"%c%d",prefix,nof); return java->getval(diapath,id); } EXPORT const char *diagui_getval ( const char *diapath, char prefix, const char *str) { char id[100]; sprintf (id,"%c%s",prefix,str); return java->getval(diapath,id); } EXPORT const char *diagui_getval (char prefix, const char *str) { return diagui_getval(NULL,prefix,str); } /* Return the current values of a field All field have an ID in the java frontend. This ID is generally a letter followed by a number. Some fields (textarea) are returned by the front-end as multiple lines with the same ID. Return the number of lines places in tb[]. */ EXPORT int diagui_getvals ( const char *diapath, char prefix, int nof, SSTRINGS &tb) { char id[100]; sprintf (id,"%c%d",prefix,nof); return java->getvals(diapath,id,tb); } EXPORT int diagui_getvals (char prefix, int nof, SSTRINGS &tb) { return diagui_getvals (NULL,prefix,nof,tb); } /* #Specification: inter-dialog messaging / principles The GUI allows independant (asynchronous, amodal) dialogs. Each dialog (or dialog set) runs in a special thread and we need a way to wake up one dialog from another, so it performs some action. This is done by sending a message. The message may be seen like a broadcast. It is sent, and zero, one or more dialog may react. Messages are sent using the function dialog_sendmessage(). Timers event are built on top of dialog_sendmessage(). The message is sent asynchronouly. This means dialog_sendmessage() return immediatly and when the caller thread yields (when blocking in DIALOG::edit for example), all interested dialogs are woke up. A message is either a string (any string you can think of) or a PRIVATE_MESSAGE. Any dialog may register interest in any string message it wants. So messages sent using string are reserved for application wide events. Dialogs unknown to the sender may wake up. Further, there is no way to make sure two independant pieces of code are not using the same string to notify unrelated dialogs. PRIVATE_MESSAGE exists to solve this. A PRIVATE_MESSAGE is simply a variable which exist solely so we can reference its memory address. Only the piece of code knowing this variable, be it either a static variable or on the heap, can send and receive messages using it. This strategy is completly safe and simple. Unrelated piece of code can't clash. Further, multiple instances of the same dialogs may communicate independantly if they create the PRIVATE_MESSAGE object dynamically. */ # /* Wakeup dialogs which are waiting for this message */ EXPORT void dialog_sendmessage(const char *msg) { // We make all dialogs receive the message. When // the dialog wakeup (in its own thread), it checks if it needs // the message. This means that most dialog wake up for nothing. // should do the trick anyway. if (dialog_mode != DIALOG_GUI){ // In text and HTML mode, there are no thread, so we record // the message immediatly lastmsg.text.setfrom (msg); }else{ for (int i=0; iget(); // Is the message already there bool found = false; for (int j=0; jmatch (dianame,msg)){ found = true; } } if (!found) messages.add (dianame,msg); } } } /* Wakeup dialogs which are waiting for this private message */ EXPORT void dialog_sendmessage(PRIVATE_MESSAGE &msg) { if (dialog_mode != DIALOG_GUI){ // In text and HTML mode, there are no thread, so we record // the message immediatly lastmsg.priv = &msg; }else{ for (int i=0; iget(); // Is the message already there bool found = false; for (int j=0; jmatch (dianame,&msg)){ found = true; } } if (!found) messages.add (dianame,&msg); } } } /* Send several messages */ EXPORT void dialog_sendmessages(PRIVATE_MESSAGES &msgs) { for (int i=0; iseconds); if (ti->deleted) break; if (ti->priv != NULL){ dialog_sendmessage (*ti->priv); }else{ SSTRING msg; msg.setfromf ("timer-%s",ti->id.get()); dialog_sendmessage (msg.get()); } if (!ti->rearm) break; } close (tb[0]); close (tb[1]); } tbtimers.remove_del (ti); } /* Locate an existing timer control using either a string ID or a private message (only one is not NULL). Return NULL if not found. */ static DIAGUI_TIMER *diagui_gettimer (const char *id, PRIVATE_MESSAGE *msg) { DIAGUI_TIMER *ret = NULL; for (int i=0; iid.cmp(id)==0) || ti->priv == msg){ ret = ti; break; } } return ret; } static void diagui_settimer ( const char *id, // ID of the timer (any string you want) PRIVATE_MESSAGE *msg, // or NULL int seconds, // number of seconds between events bool rearm) // Is this a one shot timer or it must rearm itself { /* #Specification: inter-dialog messaging / timers / restricted to GUI The current timer implementation relies on uithread which only work in GUI mode. This will have to be generalised. */ if (dialog_mode == DIALOG_GUI){ DIAGUI_TIMER *ti = diagui_gettimer (id,msg); if (ti == NULL){ ti = new DIAGUI_TIMER(id,msg,seconds,rearm); tbtimers.add (ti); uithread_ok (diagui_timerfct,(void*)ti); }else{ ti->deleted = false; ti->seconds = seconds; ti->rearm = rearm; } } } /* Create a timer which will send a message ever "seconds". Dialog interested in this timer just call the DIALOG::waitfortimer() function and it will receives MENU_MESSAGE events. You test if this was this timer which originate the message with dialog_testtimer(). */ EXPORT void dialog_settimer ( const char *id, // ID of the timer (any string you want) int seconds, // number of seconds between events bool rearm) // Is this a one shot timer or it must rearm itself { diagui_settimer (id,NULL,seconds,rearm); } /* Create a timer which will send a message ever "seconds". Dialog interested in this timer just call the DIALOG::waitfortimer() function and it will receives MENU_MESSAGE events. */ EXPORT void dialog_settimer ( PRIVATE_MESSAGE &msg, // ID of the timer int seconds, // number of seconds between events bool rearm) // Is this a one shot timer or it must rearm itself { diagui_settimer (NULL,&msg,seconds,rearm); } /* Delete a timer */ EXPORT void dialog_deltimer (const char *id) { DIAGUI_TIMER *ti = diagui_gettimer (id,NULL); if (ti != NULL) ti->deleted = true; } EXPORT void dialog_deltimer (PRIVATE_MESSAGE &msg) { DIAGUI_TIMER *ti = diagui_gettimer (NULL,&msg); if (ti != NULL) ti->deleted = true; } /* Wait few seconds, but let the other thread operate */ EXPORT void dialog_wait (int nbseconds) { PRIVATE_MESSAGE msg; dialog_settimer (msg,nbseconds,false); dialog_waitformessage (msg); } static void diagui_splash () { dialog_wait(3); diagui_sendcmd (P_Hidesplash,"\n"); } /* Present a splash screen using this optional xpm image. If xpm is NULL, the linuxconf splash screen is presented. */ EXPORT void dialog_splash (const char *xpm) { if (dialog_mode == DIALOG_GUI){ if (xpm != NULL){ char sent[PATH_MAX]; diagui_sendxpm (xpm,sent); diagui_sendcmd (P_Splash,"$xpm=%s\n",sent); }else{ diagui_sendcmd (P_Splash,"\n"); } diagui_flush(); uithread (diagui_splash); } } /* Request a dump from the dialog (even if no button was hit by the user). This function send the request and return immediatly. Later, the dummy action MENU_DUMP will be returned by the DIALOG::edit() function. The application must be ready to process other request from the front-end, which happened just about the dump. */ PUBLIC void DIALOG::request_dump() { SSTRING tmp; const char *diapath = setguiname(tmp); if (diapath != NULL){ diagui_sendcmd (P_Dump,"%s B%d\n",diapath,BUTSPC_DUMP); } } /* Same as request_dump, but wait for the MENU_DUMP event before returning. Simpler to use in applications. */ PUBLIC MENU_STATUS DIALOG::request_dumpwait() { MENU_STATUS ret = MENU_ESCAPE; SSTRING tmp; const char *diapath = setguiname(tmp); if (diapath != NULL){ diagui_sendcmd (P_Dump,"%s B%d\n",diapath,BUTSPC_DUMP); internal->listening = true; const char *dianame = setguibasename(tmp); UISTATE state; diajava_lastmousestate (state); DIAGUI_MESSAGE last = lastmsg; SSTRING path,action,menubar; int code = diagui_sync (dianame,path,action,menubar); internal->listening = false; lastmsg = last; diajava_setlastmousestate (state); ret = internal->buttons->bid2status(code); for (int i=0; igui_get(i,fieldid,actionid); } internal->gui_getdone = true; } return ret; }