#pragma implementation #include #include "guruengine.h" #include "guruengine.m" #include #include #include #include static HELP_FILE help_gurus ("guruengine","gurus"); struct GURUPATH_DRAWINFO{ GURUPATH_STATUS *status; char ctx[20]; int y; int x; }; class GURUPATH_STATUS_PRIVATE{ public: char mainid[10]; char guictx[20]; GURUPATH_DRAWINFO *dinfo; HELP_FILE *guruhelp; const char *gurutitle; PRIVATE_MESSAGE quitmsg; // The dialogs must end // so gurumanage may regain control bool mustend; // quitmsg was sent, all the gurupath // must exit. PRIVATE_MESSAGE threadend; // Ask the guruengine_buttons to end int allocid; // ID allocator for buttons bool goback; GURUPATH_STATUS *firststatus; GURUPATH_STATUS *jumpto; GURUPATH_STATUS_PRIVATE(){ mustend = false; gurutitle = NULL; guruhelp = NULL; guictx[0] = '\0'; dinfo = NULL; allocid = 0; goback = false; firststatus = NULL; jumpto = NULL; } }; PUBLIC GURUPATH_STATUS::GURUPATH_STATUS(GURUPATH_STATUS_PRIVATE *_priv) { priv = _priv; is_possible = true; // Possible by default is_filled = false; memset (tb,0,sizeof(tb)); nbpath = 0; wearehere = false; some_errors = false; nodemissing = false; level = 0; buttonid = 0; button_created = false; } PUBLIC GURUPATH_STATUS::~GURUPATH_STATUS() { for (unsigned i=0; ilevel = status.level + 1; if (i==0){ c.path1 (GURUPATH_EVAL,*st); }else if (i==1){ c.path2 (GURUPATH_EVAL,*st); }else if (i==2){ c.path3 (GURUPATH_EVAL,*st); }else if (i==3){ c.path4 (GURUPATH_EVAL,*st); }else if (i==4){ c.path5 (GURUPATH_EVAL,*st); }else if (i==5){ c.path6 (GURUPATH_EVAL,*st); } if (!st->nodemissing){ status.nbpath++; }else{ delete st; status.tb[i] = NULL; break; } } } #if 0 static void status_dump (GURUPATH_STATUS &status, int level) { printf ("%*spossible %d filled %d nb %d\n",level*4,"",status.is_possible ,status.is_filled,status.nbpath); for (int i=0; i maxlevel) maxlevel = status.level; if (status.nbpath > 0){ ret = 0; for (int i=0; i Empty white button. The node has not been filled yet. Blue button. The node is filled and the information is valid. Broken red button. The node is filled with invalid content. */ # static void gurupath_draw ( GURUPATH_STATUS &status, int lastx, int lasty, int newx, int newy) { if (!status.button_created){ status.button_created = true; status.buttonid = status.priv->allocid++; status.x = newx-6; status.y = newy-6; diagui_sendcmd (P_Inputgrid,"B%d %d %d 10 10 1 1 $track=1\n" ,status.buttonid+200,status.x,status.y); } static const char *dcblue = NULL; static const char *dcyellow = NULL; static const char *dcred = NULL; static const char *dcwhite = NULL; static const char *dcblack = NULL; if (dcblue == NULL){ const char *penblue = guiid_setpen ("blue",1,GPEN_STYLE_SOLID); const char *brushblue = guiid_setbrush ("blue",GBRUSH_STYLE_SOLID); dcblue = guiid_setdc (NULL,penblue,brushblue); const char *penred = guiid_setpen ("red",1,GPEN_STYLE_SOLID); const char *brushred = guiid_setbrush ("red",GBRUSH_STYLE_SOLID); dcred = guiid_setdc (NULL,penred,brushred); const char *penblack = guiid_setpen ("black",1,GPEN_STYLE_SOLID); const char *brushblack = guiid_setbrush ("black",GBRUSH_STYLE_SOLID); dcblack = guiid_setdc (NULL,penblack,brushblack); const char *penyellow = guiid_setpen ("yellow",1,GPEN_STYLE_SOLID); const char *brushyellow = guiid_setbrush ("yellow",GBRUSH_STYLE_SOLID); dcyellow = guiid_setdc (NULL,penyellow,brushyellow); const char *penwhite = guiid_setpen ("white",1,GPEN_STYLE_SOLID); const char *brushwhite = guiid_setbrush ("white",GBRUSH_STYLE_SOLID); dcwhite = guiid_setdc (NULL,penwhite,brushwhite); } const char *dc = dcblack; if (status.is_possible){ if (status.is_filled){ dc = dcblue; } }else{ dc = dcred; } diagui_sendcmd (P_Drawline,"%d %d %d %d $dc=%s\n",lastx,lasty,newx,newy ,dc); if (status.nbpath > 0){ if (status.nbpath == 1){ gurupath_draw (*status.tb[0],newx,newy,newx+30,newy); }else{ int tbh[status.nbpath],total=0; for (int i=0; ictx); diagui_sendcmd (P_Clear,"\n"); gurupath_draw (*dinfo->status,dinfo->x,dinfo->y,dinfo->x+30,dinfo->y); diagui_sendcmd (P_Refresh,"\n"); diagui_sendcmd (P_End,"\n"); } /* Format a long text into many labels. Expand tabs. */ static void gurupath_formatintro (DIALOG &dia, const char *text) { while (*text != '\0'){ char line[100+8+1]; char *dst = line; int col = 0; while (*text != '\n' && *text != '\0' && col < 100){ char car = *text++; if (car == '\t'){ *dst++ = ' '; col++; while (col % 8 != 0){ *dst++ = ' '; col++; } }else{ *dst++ = car; col++; } } *dst = '\0'; if (*text == '\n') text++; char tmp[1000]; dia.gui_label ("%s",diagui_quote(line,tmp)); dia.gui_dispolast (GUI_H_LEFT,10,GUI_V_CENTER,1); dia.newline(); } } void _F_gurupath::setintro (const char *text) { priv->intro.setfrom (text); gurupath_formatintro (*priv->dia,text); } void _F_gurupath::settitle (const char *title) { priv->status->title.setfrom (title); } void _F_gurupath::seticon (const char *icon) { priv->has_icon = true; char name_sent[PATH_MAX]; diagui_sendxpm (icon,name_sent); priv->dia->gui_passthrough (P_Icon_xpm,"%s",name_sent); priv->dia->gui_passthrough (P_Form,"form"); } void _F_gurupath::attention() { seticon ("Notice"); } void _F_gurupath::allfine () { seticon ("Allfine"); } /* Change the current field. This is called before calling the edit() function. */ void _F_gurupath::setcursor (int nof) { priv->nof = nof; } bool _F_gurupath::edit (HELP_FILE &help) { if (priv->has_icon){ priv->dia->gui_end(); priv->has_icon = false; } while (1){ if (dialog_mode == DIALOG_GUI){ priv->code = priv->dia->edit ("","",help,priv->nof ,priv->butmask); }else{ priv->code = priv->dia->edit (priv->status->title.get(),priv->intro.get(),help,priv->nof ,priv->butmask); } if (priv->code >= MENU_MESSAGE){ break; }else if (priv->code >= MENU_USR3){ dobutton (priv->tbid[priv->code - MENU_USR3]); priv->dia->reload(); }else{ break; } } bool ret = priv->code == MENU_USR2; if (ret) priv->dia->save(); return ret; } bool _F_gurupath::edit () { return edit (help_nil); } void _F_gurupath::dobutton(int) { } void _F_gurupath::setbutinfo (int id, const char *s1, const char *s2) { priv->dia->setbutinfo (MENU_USR3+priv->nbid,s1,s2); priv->tbid[priv->nbid] = id; priv->butmask |= (MENUBUT_USR3 << priv->nbid); priv->nbid++; } /* Record the information about a terminal node */ void _F_gurupath::setterminal (const char *shorttext, const char *longtext) { priv->status->terminal.shorttext.setfrom (shorttext); priv->status->terminal.longtext.setfrom (longtext); } void gurupath( _F_gurupath &c, GURUPATH_MODE mode, GURUPATH_STATUS &status) { gurupath_private priv(status); c.priv = &priv; if (mode == GURUPATH_EVAL){ gurupath_eval (c,status); }else{ if (status.priv->jumpto == &status) status.priv->jumpto = NULL; while (!status.priv->mustend){ status.wearehere = true; if (status.priv->jumpto == NULL) gurupath_draw (status.priv->dinfo); status.wearehere = false; if (status.priv->jumpto != NULL){ // The user clicked on a dialog, but this is not the // target of the jump, so we simulate a "next" button priv.code = MENU_USR2; }else{ DIALOG dia; dia.waitfor (status.priv->quitmsg); dia.set_formparms ("vtrigger=300"); if (status.priv->guruhelp != NULL){ dia.addhelp (*status.priv->guruhelp,status.priv->gurutitle); } dia.addhelp (help_gurus,MSG_U(I_GURUSINTRO,"Gurus general introduction")); dia.setcontext (status.priv->guictx); priv.butmask = MENUBUT_USR2; if (status.level > 0){ dia.setbutinfo (MENU_USR1,MSG_U(B_BACK,"Back"),MSG_R(B_BACK)); priv.butmask |= MENUBUT_USR1; }else{ priv.butmask |= MENUBUT_CANCEL; } dia.setbutinfo (MENU_USR2,MSG_U(B_NEXT,"Next"),MSG_R(B_NEXT)); { static const char *dc = NULL; if (dc == NULL){ const char *font = guiid_setfont (20,GFONT_ID_MODERN,GFONT_STYLE_DEFAULT,GFONT_WEIGHT_BOLD,false); const char *pen = guiid_setpen ("blue",1,GPEN_STYLE_SOLID); const char *brush= guiid_setbrush ("blue",GBRUSH_STYLE_SOLID); dc = guiid_setdc (font,pen,brush); } char tmp[1000]; dia.gui_passthrough (P_Label,"%s $dc=%s" ,diagui_quote(status.title.get(),tmp),dc); dia.gui_dispolast (GUI_H_CENTER,2,GUI_V_CENTER,1); dia.newline(); //priv->title.setfrom (title); } priv.dia = &dia; c.dialog (dia); priv.dia = NULL; } if (priv.code == MENU_CANCEL || priv.code == MENU_ESCAPE){ status.priv->mustend = true; break; }else if (priv.code == MENU_MESSAGE){ // Ok, we have to quit status.priv->mustend = true; break; }else if (priv.code == MENU_USR1){ status.priv->goback = true; break; }else if (priv.code == MENU_USR2){ gurupath_eval (c,status); // status_dump(status,0); if (status.nbpath > 0){ int i; for (i=0; ipriv->goback = false; if (st->is_possible){ if (i==0){ c.path1 (mode,*st); }else if (i==1){ c.path2 (mode,*st); }else if (i==2){ c.path3 (mode,*st); }else if (i==3){ c.path4 (mode,*st); }else if (i==4){ c.path5 (mode,*st); }else if (i==5){ c.path6 (mode,*st); } if (st->priv->goback || st->priv->mustend){ break; } } } if (i == status.nbpath) break; }else{ //if (!priv.terminal.is_empty()){ //} break; } } } } } /* Locate the node having the given button id */ static GURUPATH_STATUS *guruengine_locate ( GURUPATH_STATUS *first, int id, bool &possible) // Must be true originally, will turn false // if any node along the path to the node we search // is not possible (our node is not reachable either) { GURUPATH_STATUS *ret = NULL; if (!first->is_possible) possible = false; if (first->buttonid == id){ ret = first; }else{ for (int i=0; inbpath; i++){ GURUPATH_STATUS *st = first->tb[i]; bool tmp=true; ret = guruengine_locate (st,id,tmp); if (ret != NULL){ if (!tmp) possible = false; break; } } } return ret; } /* This function monitors buttons and send messages */ static void guruengine_buttons (void *p) { GURUPATH_STATUS_PRIVATE *priv = (GURUPATH_STATUS_PRIVATE *)p; while (1){ SSTRING action,path,menubar; int code = diagui_sync (priv->mainid,path,action,menubar); if (code == 99){ priv->mustend = true; dialog_sendmessage (priv->quitmsg); }else if (dialog_testmessage (priv->threadend)){ // Send an acknowledge dialog_sendmessage (priv->threadend); break; }else if (code >= 200){ UISTATE st; diajava_lastmousestate (st); int id = code - 200; // We have to locate this node bool possible = true; GURUPATH_STATUS *node = guruengine_locate(priv->firststatus,id ,possible); if (node != NULL){ //fprintf (stderr,"mouse title %s %d\n",node->title.get(),possible); { // We must draw or erase this node title int x = node->x; int y = node->y; const char *text = ""; if (st.x >= x && st.x < x+10 && st.y >= y && st.y < y+10){ // We are inside, we put the title text = node->title.get(); } char tmp[1000]; diagui_sendcmd (P_Setval,"%s quick %s\n" ,priv->mainid ,diagui_quote(text,tmp)); } if (st.leftb){ // We must jump to this node if (!possible){ xconf_error (MSG_U(E_NOTREACH ,"This node can't be reached\n" "Some information along the path is either\n" "incomplete, or incompatible")); }else{ priv->jumpto = node; // We have to quit editing back to the top gurumanage // and then find our way to the proper node // priv->jumpto tells us we are not quitting priv->mustend = true; dialog_sendmessage (priv->quitmsg); } }else if (st.rightb){ // Some popup menu ? } } } } } /* This is the main function used to initiate a gurus. It has a single tag called "exec". You connect to it either a gurupath component or any variations (gurusteps, guruiter). Return -1 if the user aborted the process */ int gurumanage(_F_gurumanage &c, const char *title, const char *intro, HELP_FILE &help) { dialog_clear(); GURUPATH_STATUS_PRIVATE priv; GURUPATH_STATUS status (&priv); priv.firststatus = &status; c.exec (GURUPATH_EVAL,status); // status_dump (status,0); int nbcol=0; int nbline = gurupath_evaldim (status,nbcol); int height = (nbline-1)*30+40; sprintf (priv.mainid,"guru-%d",uithread_id); // Enable uithread again, see uithread() ui_context.treemenu_level = ui_context.treejump_level + 1; if (dialog_mode == DIALOG_GUI) uithread (guruengine_buttons,&priv); sprintf (priv.guictx,"%s.dialog",priv.mainid); char tmp[1000]; diagui_sendcmd (P_MainForm,"%s %s\n",priv.mainid,diagui_quote(title,tmp)); diagui_sendcmd (P_Label,"\"\" $id=quick\n",height-10); diagui_sendcmd (P_Dispolast,"c 1 t 1\n"); diagui_sendcmd (P_Newline,"\n"); diagui_sendcmd (P_Form,"map $w=500 h=%d\n",height); diagui_sendcmd (P_End,"\n"); diagui_sendcmd (P_Newline,"\n"); diagui_sendcmd (P_Form,"dialog $w=500 h=400\n"); diagui_sendcmd (P_End,"\n"); diagui_sendcmd (P_End,"$nopopup=1\n"); GURUPATH_DRAWINFO drawinfo; sprintf (drawinfo.ctx,"%s.map",priv.mainid); drawinfo.status = &status; drawinfo.x = 30; drawinfo.y = height/2; priv.dinfo = &drawinfo; gurupath_draw (status.priv->dinfo); diagui_sendcmd (P_Show,"%s 1\n",priv.mainid); MENU_STATUS code; { DIALOG dia; dia.waitfor (priv.quitmsg); dia.set_formparms ("vtrigger=300"); dia.setcontext (priv.guictx); const char *text_intro = ""; if (dialog_mode == DIALOG_GUI){ #if 0 gurupath_formatintro (dia,MSG_U(I_GURUINTRO ,"You will be presented various dialogs. They are mapped\n" "along the map above. Depending on your answers,\n" "you will visit apropriate areas of the map.\n" "\n" "The drawing above (the map) represent the path to follow.\n" " -The yellow X represent your position.\n" " -Red lines indicate non reachable areas.\n" " -blue lines indicate already filled dialogs.\n" " -black lines represent area to visit.\n" )); dia.gui_label ("\"\""); dia.newline(); dia.gui_label ("\"\""); dia.newline(); #endif gurupath_formatintro (dia,intro); }else{ text_intro = intro; } dia.setbutinfo (MENU_USR1,MSG_R(B_NEXT),MSG_R(B_NEXT)); int nof = 0; dia.addhelp (help_gurus,MSG_R(I_GURUSINTRO)); code = dia.edit (title,text_intro,help,nof,MENUBUT_CANCEL|MENUBUT_USR1); if (code == MENU_MESSAGE && status.priv->jumpto != NULL){ code = MENU_USR1; } } int ret = -1; if (code == MENU_USR1){ priv.guruhelp = NULL; priv.gurutitle = title; if (!help.is_nil()) priv.guruhelp = &help; while (1){ c.exec (GURUPATH_EDIT,status); if (status.priv->mustend){ status.priv->mustend = false; if (status.priv->jumpto == NULL) break; }else if (status.priv->goback){ break; }else{ // Ok, the user has run the guru to completion ret = 0; break; } } } diagui_sendcmd (P_Delete,"%s\n",priv.mainid); dialog_sendmessage (priv.threadend); dialog_waitformessage (priv.threadend); return ret; } /* Use this function at the end of a branch */ int gurupath_confirm ( GURUPATH_MODE mode, GURUPATH_STATUS &status, const char *branch_title, // The short title at the end // of a branch on the map const char *text) // Some longer confirmation) { status.is_possible = true; status.is_filled = false; status.some_errors = false; status.terminal.shorttext.setfrom (branch_title); if (mode == GURUPATH_EDIT){ xconf_notice ("%s",text); } return 0; }