#include #include #include #include #include #include #include #include "dialog.m" #include // #Specbeg: dialog / sample code / records /* This shows how to handle table of records. This is pretty much what linuxconf is doing all around. */ class PERSON: public ARRAY_OBJ{ public: SSTRING name; SSTRING surname; SSTRING tel; PERSON(const char *_name, const char *_surname, const char *_tel){ name.setfrom (_name); surname.setfrom (_surname); tel.setfrom (_tel); } }; class PERSONS: public ARRAY{ public: PERSON *getitem(int no) const{ return (PERSON*)ARRAY::getitem(no); } }; static void sample_records() { // Fill few records PERSONS tb; tb.add (new PERSON("Gelinas","Jacques","111-2222")); tb.add (new PERSON("Who","John","222-2222")); tb.add (new PERSON("Red","Jessy","333-2222")); // DIALOG_RECORDS dia; dia.newf_head ("","Name\tSurname\tTelephone"); int nof = 0; while (1){ for (int i=0; isurname.get(),p->tel.get()); dia.set_menuitem (i,p->name.get(),buf); } // Remove extra fields // dia.getnb() includes the header dia.remove_last (tb.getnb()+1); MENU_STATUS code = dia.editmenu ("title","introduction",help_nil ,nof,MENUBUT_ADD|MENUBUT_DEL); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else if (code == MENU_DEL){ if (xconf_delok()){ tb.remove_del (0); } }else if (code == MENU_ADD){ static int add=1; // Add arbitrary fields char buf[10]; sprintf (buf,"%d",add++); tb.add (new PERSON(buf,buf,buf)); }else{ xconf_notice ("Edit field %d",nof); } } } // #Specend: // #Specbeg: dialog / sample code / inter dialog messaging /* This creates two independant dialogs which are exchanging messages. In this example, the same code is used to create the dialog, except that we exchange the message sent and received by each dialog. */ static void sample_dialog(PRIVATE_MESSAGE &send, PRIVATE_MESSAGE &receive) { DIALOG dia; dia.waitfor (receive); SSTRING s; dia.newf_str ("A string",s); dia.setbutinfo (MENU_USR1,"Send","Send"); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("title","The field grows" ,help_nil,nof ,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_MESSAGE){ if (dialog_testmessage(receive)){ dia.save(); s.append("X"); dia.reload (); } }else if (code == MENU_USR1){ dialog_sendmessage (send); }else{ // Do nothing with s, just exit break; } } } static PRIVATE_MESSAGE p1,p2; static void sample_fct (void *) { sample_dialog (p2,p1); } static void sample_messages() { uithread (sample_fct,NULL); sample_dialog (p1,p2); } // #Specend: // #Specbeg: dialog / sample code / private messaging /* This creates 4 dialogs, grouped two by two. Each dialog may send a message to its corresponding dialog. The two pairs are independant. Yet they are using exactly the same code. This shows how PRIVATE_MESSAGE may be used to achieve private communication. */ struct PMSGS{ PRIVATE_MESSAGE p1; PRIVATE_MESSAGE p2; }; static void sample_fct1 (void *data) { PMSGS *p = (PMSGS*)data; sample_dialog (p->p2,p->p1); } static void sample_fct2 (void *data) { PMSGS *p = (PMSGS*)data; sample_dialog (p->p1,p->p2); } /* This function creates two independant dialog which are exchanging messages. */ static void sample_privates() { PMSGS group1; PMSGS group2; uithread (sample_fct1,&group1); uithread (sample_fct2,&group1); uithread (sample_fct1,&group2); sample_dialog (group2.p1,group2.p2); } // #Specend: // #Specbeg: dialog / sample code / timers /* This dialog presents a single numeric field, which grows by itself every 2 seconds */ static void sample_timer() { DIALOG dia; int num = 0; dia.newf_num ("Some value",num); PRIVATE_MESSAGE msg; dialog_settimer(msg,2,false); dia.waitfortimer (msg); dia.setbutinfo (MENU_USR1,"stop timer","stop timer"); dia.setbutinfo (MENU_USR2,"start timer","start timer"); int nof=0; while (1){ MENU_STATUS code = dia.edit ("title","The field grows" ,help_nil,nof ,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1|MENUBUT_USR2); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_MESSAGE){ if (dialog_testtimer(msg)){ num++; dia.reload (); } }else if (code == MENU_USR1){ dialog_deltimer(msg); }else if (code == MENU_USR2){ dialog_settimer(msg,2,true); }else{ xconf_notice ("num = %d",num); break; } } dialog_deltimer(msg); } // #Specend: // #Specbeg: dialog / sample code / reloading /* This dialog shows how you can change the content of a field while the dialog is running. The dialog present 5 check boxes, initially un-selected. Two extra buttons are added: All and None. The All button selects all check-boxes and the None button un-select them. The dialog also present few text fields and enum. The clear and fill button act on them. Note that the DIALOG::reload() function accept 0 or one argument. The 0 argument version reloads all field input buffer from their corresponding variable while the other one reload a single field. */ static void sample_reload() { DIALOG dia; char tb[5]; for (int i=0; i<5; i++){ char opt[20]; sprintf (opt,"Option %d",i+1); tb[i] = 0; dia.newf_chk (opt,tb[i],"is active"); } SSTRING text,combo,list("opt1"); int listn=0; dia.newf_str ("A text field",text); FIELD_COMBO *fcombo = dia.newf_combo ("A combo",combo); fcombo->addopt ("opt1"); fcombo->addopt ("opt2"); fcombo->addopt ("opt3"); FIELD_LIST *flist = dia.newf_list ("A list",list); flist->addopt ("opt1"); flist->addopt ("opt2"); flist->addopt ("opt3"); FIELD_ENUM *fenum = dia.newf_enum ("A enum",listn); fenum->addopt ("opt1"); fenum->addopt ("opt2"); fenum->addopt ("opt3"); dia.setbutinfo (MENU_USR1,"All","All"); dia.setbutinfo (MENU_USR2,"None","None"); dia.setbutinfo (MENU_USR3,"Clear","Clear"); dia.setbutinfo (MENU_USR4,"Fill","Fill"); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("title","",help_nil ,nof,MENUBUT_ACCEPT|MENUBUT_CANCEL |MENUBUT_USR1|MENUBUT_USR2|MENUBUT_USR3|MENUBUT_USR4); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_USR1 || code == MENU_USR2){ memset (tb,code == MENU_USR1 ? 1 : 0, sizeof(tb)); dia.reload(); }else if (code == MENU_USR3){ text.setempty(); combo.setempty(); list.setfrom("opt1"); listn = 0; dia.reload(); }else if (code == MENU_USR4){ text.setfrom ("Some text"); combo.setfrom ("An option"); list.setfrom ("opt2"); listn = 1; dia.reload(); }else{ xconf_notice ("text=%s\ncombo=%s\nlist=%s\nenum=%d" ,text.get(),combo.get(),list.get(),listn); break; } } } // #Specend: // #Specbeg: dialog / sample code / UI threads /* Linuxconf UI toolkit is based on classical programming. This is sometime called modal programming. This kind of programming is much simpler as opposed to event driven (such as most GUI toolkit). It can be presented by the following pseudo code: # setup input field while (1){ call the edit function process the user actions (button press) } # In a single function, one can create and handle a complete multi-field dialog. Much more simpler (code efficient). But it has a drawback. Once a dialog has called the edit function the program is locked there. So you can't have multiple dialog operating at once. This limitation has been solved using the User Interface Threads (UI threads). UI threads are keeping the simplicity of classical programming while removing the major limitation. This has been used primarily for the linuxconf project. Linuxconf is made of many many dialogs, so it pays to have this as simple as possible. We believe that this strategy could be used for more GUI oriented application as well though. The following applicaiton shows two independant contexts working at once. */ static void sample_context() { while (1){ if (dialog_yesno("title","Are you sure",help_nil)==MENU_YES){ xconf_notice("Really"); }else{ xconf_notice("This shows"); break; } } } static void sample_thread() { uithread(sample_context); sample_context(); } // #Specend: // #Specbeg: dialog / sample code / UI threads / passing information /* There are two ways to pass information to a thread. The implicit way is to use glocal variables (or static variables outside function body). Threads live in the same process so have the same access. The other solution is to pass private data using the uithread optional second argument. The argument is a void pointer, so you can pretty much pass anything you want this way. There is a little catch though. The information passed must continue to exist, potentially after the calling thread exits. So the void pointer can't point to local variable in the caller thread. Well, they can but you must be sure the new threads will exits before the caller. The solution to this problem is to always pass information allocated on the heap, using malloc, strdup, or new and let the new thread delete it when done. It general, it is better to have the allocator handle the de-allocation (cleaner), but in this case, it is much simpler this way. Here is an example. */ static void sample_context_with_data(void *data) { char *msg = (char *)data; while (1){ if (dialog_yesno("title",msg,help_nil)==MENU_YES){ xconf_notice("Really"); }else{ xconf_notice("This shows"); break; } } free (msg); } static void sample_threaddata() { uithread(sample_context_with_data,strdup("Are you sure")); uithread(sample_context_with_data,strdup("Are you really sure")); xconf_notice ("Hello world"); } // #Specend: // #Specbeg: dialog / sample code / help list /* Linuxconf supports a special case of help list where the available values are presented with a small description. This is used for FIELD_COMBO, FIELD_LIST and FIELD_ENUM. FIELD_COMBO comes with a help list, but the user may type anything he wants. FIELD_LIST restricts the user to the available choice. The edit variable is a SSTRING. FIELD_ENUM works like FIELD_LIST, but the variable is an integer. The value correspond to the index in the help list. FIELD_LIST also has support for translation. The value presented in the help list may be translated (different language) and the input buffer is automatically switched back and forth between the shown (translated) value and the effective one. Here is a simple dialog showing all cases */ static void sample_helplist() { DIALOG dia; SSTRING path; FIELD_COMBO *comb = dia.newf_combo("Enter a path",path); comb->addopt ("","Nothing"); comb->addopt ("/tmp","Temporary files directory"); comb->addopt ("/var","System data directory"); comb->addopt ("/etc","Configuration directory"); // Use may pick the paper type in french, but the outcome // is one of the value letter or ledger. The variable // paper originally is set to letter, but the user will see that // it is set to Lettre. SSTRING paper ("letter"); FIELD_LIST *list = dia.newf_list("Paper size",paper); // In general, the first argument is a fixed string, while // the second and third are translatable string (using MSG_U() macro) list->addopt ("","Default","Valeur par default"); list->addopt ("letter","Lettre","Papier 8.5 x 11 standard"); list->addopt ("ledger","Legal","Papier légal 8.5 x 14"); SSTRING feature("-1"); list = dia.newf_list("Feature:", feature); list->addopt("-1","Not avail.",""); list->addopt("", "Use default definition", ""); list->addopt("1", "Enabled", ""); list->addopt("0", "Disabled", ""); // here, we do not care about the strings. We want to know // the index of the selected string. int sel = 2; FIELD_ENUM *enm = dia.newf_enum ("Printer type",sel); enm->addopt ("HP4"); enm->addopt ("HP5"); enm->addopt ("HP6"); enm->addopt ("HP7"); int nof = 0; if (dia.edit ("Selections","",help_nil,nof)==MENU_ACCEPT){ xconf_notice ("You have selected\n" "Directory path: %s\n" "Paper type : %s\n" "Feature : %s\n" "Printer number: %d\n" ,path.get(),paper.get(),feature.get(),sel); } } // #Specend: // #Specbeg: dialog / sample code / help dialog /* You can associate a dialog with a field. Instead of a help list triggered by a button at the right end of the field, you trigger some code provided by the application. One possible usage is a file browser used to help enter a file name or path. The strategy provides maximum flexibility for the helper dialog. It can even cancel the calling dialog. The strategy is built on top of the inter dialog messaging. Since you generally want to signal your own dialog, you are using PRIVATE_MESSAGE objects. */ # /* This is not a real helper dialog to select a path but it shows you can connect any dialog you want, any processing you want */ static void sample_helper (const char *title, SSTRING &path) { DIALOG dia; char sel = 0; static const char *tb[]={ "/tmp","/var","/root" }; for (int i=0; i<3; i++){ dia.newf_radio ("",sel,i,tb[i]); } int nof = 0; if (dia.edit (title,"",help_nil,nof)==MENU_ACCEPT){ path.setfrom (tb[sel]); } } static void sample_helpdialog() { DIALOG dia; SSTRING path1,path2; PRIVATE_MESSAGE msg1,msg2; dia.newf_str("Enter a path",path1); dia.set_helpdia (msg1); dia.newf_str("Enter another path",path2); dia.set_helpdia (msg2); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("Paths","",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_ACCEPT){ xconf_notice ("You have selected\n" "Directory path1: %s\n" "Directory path2: %s\n",path1.get(),path2.get()); break; }else if (code == MENU_MESSAGE){ // It is possible to use the same message for several field // if you want, using "nof" to tell which dialog to trigger if (dialog_testmessage (msg1)){ sample_helper ("path1",path1); dia.reload (nof); }else if (dialog_testmessage (msg2)){ sample_helper ("path2",path2); dia.reload (nof); } } } } // #Specend: // #Specbeg: dialog / sample code / help context /* The HELP_CONTEXT object allows you to setup a hierarchy of related help files. In general, when you create a help file associated with a dialog, it provides "to the point" information about this dialog. When you setup a help for the main menu of a module, you try to provide an overview of the module. Unfortunatly, this is kind of lost when you access the help of a given dialog. One solution is to write larger general purpose help for everything in the module. In general, with such a help, the user (at least me) never find (or hardly) find what it is looking for. Another solution would be to have various pointers at the end of every help pointing to related topics. We will need that at some point. The HELP_CONTEXT object is another solution. You setup various HELP_CONTEXT local objects (local variables) and every HELP_CONTEXT along the path (code path) going to a dialog will be included as an alternative help. In general, the user will have access to a specific help, then the main menu menu help and the about_this_module help. This way, users will learn more easily all the features a module is providing. */ static void sample_helpctx3(const char *title) { DIALOG dia; SSTRING f; dia.newf_str ("field1",f); int nof = 0; HELP_FILE help_context ("dialog","context"); while (1){ MENU_STATUS code = dia.edit (title,"Click help" ,help_context,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (f.is_empty()){ xconf_error ("field1 may not be empty"); }else{ break; } } } struct SAMPLE_HELP_INFO{ int no1,no2; SAMPLE_HELP_INFO(int x,int y){ no1 = x; no2 = y; } }; static void sample_helpctx2(void *data) { SAMPLE_HELP_INFO *inf = (SAMPLE_HELP_INFO*)data; SSTRING title; title.setfromf ("help_context sub menu %d.%d",inf->no1,inf->no2); HELP_CONTEXT h1 (title.get(),"dialog","mainmenu_context"); title.setfromf ("DIALOG %d.%d",inf->no1,inf->no2); sample_helpctx3(title.get()); delete inf; } static void sample_helpctx1(void *data) { int no = (int)data; SSTRING title; title.setfromf ("help_context menu %d",no); HELP_CONTEXT h1 (title.get(),"dialog","about_context"); for (int i=1; i<3; i++){ uithread (sample_helpctx2,(void*)new SAMPLE_HELP_INFO(no,i)); } sample_helpctx2((void*)new SAMPLE_HELP_INFO(no,0)); } static void sample_helpcontext() { HELP_CONTEXT h1 ("about help_context","dialog","about_context"); for (int i=1; i<4; i++){ uithread (sample_helpctx1,(void*)i); } sample_helpctx1 ((void*)0); } // #Specend static void sample_dialogtricks() { DIALOG dia; SSTRING f1; dia.newf_str ("field 1",f1); dia.last_noempty(); int nof=0; while (1){ MENU_STATUS code = dia.edit("title","",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ // Process the values break; } } } // #Specbeg: dialog / sample code / using newf_chkm_num static void sample_chkmvals() { DIALOG dia; // Numerical input int val1 = 3000, val2 = 10,val3=256; static int vals[]={10,20,30}; static const char *opts[]={"default","opt20","opt30",NULL}; dia.newf_chkm_num ("options 1",val1,3600,vals,opts); dia.newf_chkm_num ("options 2",val2,10,vals,opts); dia.newf_chkm_hexnum ("options 3",val3,10,vals,opts); // String input. This combines some options and an extra string // value. SSTRING str("Default string"); int val = 1; int str_vals[] = {0,1,2}; const char *titles[] = {"Option 1","Option 2",NULL}; dia.newf_chkm_str("Prompt",val,str,str_vals,titles); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("chkmvals","",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ xconf_notice ("The numeric values are %d %d %x",val1,val2,val3); if (val == 2){ xconf_notice("A string was entered :'%s'",str.get()); }else{ xconf_notice("A numerical input was entered : %d",val); } break; } } } // #Specend: // #Specbeg: dialog / sample code / using textarea /* A textarea widget in text mode must be enter by typing a space. All other keys simply walk over the field. To escape, hit escape twice. */ static void sample_textarea() { DIALOG dia; SSTRING txt; txt.append ("This is the first line.\n"); txt.append ("This is the second line.\n"); txt.append ("And the third one.\n"); dia.newf_textarea ("Some text",txt,60,10); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("A text","",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ xconf_notice ("The value is:\n%s",txt.get()); break; } } } // #Specend: // #Specbeg: dialog / sample code / using textarea and other field static void sample_textarea_mixed() { DIALOG dia; SSTRING f[15]; for (int i=0; i<10; i++){ char tmp[10]; sprintf (tmp,"field %d",i+1); dia.newf_str (tmp,f[i]); } SSTRING txt; txt.append ("This is the first line.\n"); txt.append ("This is the second line.\n"); txt.append ("And the third one.\n"); dia.newf_textarea ("Some text",txt,60,10); for (int i=10; i<15; i++){ char tmp[10]; sprintf (tmp,"field %d",i+1); dia.newf_str (tmp,f[i]); } int nof = 0; while (1){ MENU_STATUS code = dia.edit ("A text","",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ xconf_notice ("The value is:\n%s",txt.get()); break; } } } // #Specend: // #Specbeg: dialog / sample code / using GUI passthrough /* Linuxconf dialogs are generally presented uniformly, using a simple layout. Using a single source, one produces a dialog operating in text mode, graphical and web. Cool! But one may wish to invest a little bit more in the graphical look of his dialog. The gui_passthrough is there for that. It is not terribly difficult to use, but one must understand the GUI protocol (using to communicate with the GUI front-end). It is documented at The various P_xxxx primitives are defined in proto.h. Once you use the DIALOG::gui_passthrough(), DIALOG::newline() or any DIALOG::gui_xxx() function, you are on your own. You control the layout of every field and must use DIALOG::newline() on a regular basis. The following dialog presents two groups. One contains various input field. The other contains a single field surrounded by labels. */ static void sample_guipassthrough() { if (dialog_mode != DIALOG_GUI){ xconf_error ("Only works in GUI mode"); }else{ DIALOG dia; SSTRING s1,s2,s3,s4; dia.gui_group ("Group 1"); dia.newf_str ("Field 1",s1,10); dia.newline(); // No label in front of the field. s2 will be under // the "Field 1" label. s3 will be under s1 dia.newf_str (NULL,s2); dia.newf_str (NULL,s3,10); dia.gui_end (); dia.gui_group ("Group 2"); // First line dia.gui_label ("Label1"); dia.gui_label ("Label2"); dia.gui_label ("Label3"); dia.gui_passthrough (P_Label,"Label4\n"); dia.newline(); // Second line, 1 label, one field covering 2 columns and // a label. dia.gui_label ("Label1"); dia.newf_str (NULL,s4,20); dia.gui_dispolast (GUI_H_CENTER,2,GUI_V_CENTER,1); dia.gui_label ("Label4"); dia.newline(); dia.gui_label ("Label1"); dia.gui_label ("Label2"); dia.gui_label ("Label3"); dia.gui_label ("Label4"); dia.gui_end(); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("GUI layout","",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ break; } } } } // #Specend: // #Specbeg: dialog / sample code / using auto_newline /* We can invest a little in the graphical appearance of a dialog by using DIALOG::newline() and the various DIALOG::gui_passthrough() (DIALOG::gui_label,DIALOG::gui_end,DIALOG::gui_group, DIALOG::gui_dispolast). But once you use any of those, the layout manager turn of its auto-newline feature. Now, if it was just to enhance one page of a complex notebook dialog, this is annoying. Having to insert DIALOG::newline() everywhere is a little too much work. The function DIALOG::auto_newline() lets you control the layout manager. If you use this function only once in a dialog, the layout manager will listen. If you use it only once in a dialog, the layout manager start in auto-newline mode and let you switch it off and on. DIALOG::auto_newline() inserts a dummy field in the dialog, so you may have to deal with that if you expect to jump to a specific field. DIALOG::auto_newline() has no effect in text or HTML mode. */ static void sample_autonewline() { // Auto-newline is on by default if there is no DIALOG::newline // or DIALOG::gui, or if DIALOG::auto_newline() is used once. DIALOG dia; dia.newf_title ("simple page",1,"","simple page"); SSTRING s; dia.newf_str ("field 1",s); dia.newf_str ("field 2",s); dia.newf_str ("field 3",s); dia.newf_str ("field 4",s); dia.newf_title ("complex page",1,"","complex page"); // Ok, now the complex layout, we turn off auto-layout dia.auto_newline(false); dia.gui_label(""); dia.gui_label("Column1"); dia.gui_label("Column2"); dia.gui_label("Column3"); dia.newline(); for (int i=0; i<4; i++){ dia.gui_label ("Row%d",i+1); for (int j=0; j<3; j++){ dia.newf_str (NULL,s,10); } dia.newline(); } // Back to auto-newline mode dia.auto_newline(true); dia.newf_title ("simple page",1,"","simple page"); dia.newf_str ("field 1",s); dia.newf_str ("field 2",s); dia.newf_str ("field 3",s); dia.newf_str ("field 4",s); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("Using auto-newline" ,"The first page use a simple layout\n" "but the second is present as a grid" ,help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ break; } } } // #Specend: // #Specbeg: dialog / sample code / mapping fields in several pages /* Large dialogs are often annoying to use. You must use the scroll bar to review the various fields. Dialogs are often splitted in several section, with title between each. Using the DIALOG::newf_title one can easily enhance the look of a large dialog. It distributes the field in pages of a notebook object. Sub-notebook are even supported (as seen in the user account dialog, with the various privilege sections). The following example presents this. */ static void sample_setpage (DIALOG &dia, SSTRING s[], int start, int stop) { for (int i=start; i # Some comments var1="Some value" # more comments other_variable="value" var2="value" The small dialog will only touch var1 and var2 and preserve the comments and the ordering of sample.data. This is really the goal of linuxconf to provide some user interface for various configuration file. The bulk of the code goes like that Parsing the configuration files. Presenting the dialog. Updating the configuration file. Sometime, it is done using several functions, various C++ classes. It can be complex. The goal of the registry is to be able to reuse this functionality */ static void sample_registrydialog() { DIALOG dia; SSTRING s1,s2; CONFIG_FILE f_config ("/tmp/sample.data",help_nil,CONFIGF_OPTIONAL); VIEWITEMS items; items.read (f_config); char tmp[1000]; s1.setfrom (items.locateval ("var1",tmp)); s2.setfrom (items.locateval ("var2",tmp)); dia.newf_str (MSG_U(F_SAMPLEFIELD1,"Sample field1"),s1); dia.newf_str (MSG_U(F_SAMPLEFIELD2,"Sample field2"),s2); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("Sample data","",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ // We must update the file items.update ("var1",s1); items.update ("var2",s2); items.write (f_config,NULL); break; } } } /* Here we publish the two variables. As you can see, this is very simple. Each variable (var1, var2) is associated with a dialog ID (NULL here and most of the time), a field prompt and a trigger function. This function simply calls the dialog */ #include "modregister.h" static PUBLISH_VARIABLES_MSG sample_var_list1[]={ {"var1",P_MSG_R(F_SAMPLEFIELD1)}, {"var2",P_MSG_R(F_SAMPLEFIELD2)}, { NULL } }; static REGISTER_VARIABLES sample_registry1("sample",sample_var_list1 ,NULL,sample_registrydialog); /* This function perform the following sequence Execute the dialog so you look at it. Execute it again so you can see that your input was saved. Retrieve var1 and var2 using the registry. Setting var1 and var2 to new values using the registry. Execute the dialog so you can see the new values. */ static void sample_useregistry() { xconf_notice ("Ok, we call the dialog to edit /tmp/sample.data\n" "Enter some values and accept"); sample_registrydialog(); xconf_notice ("We call it again to see if the data was saved properly"); sample_registrydialog(); // Now we will retrieve the values master_registry.start_session(); SSTRING v1 (master_registry.get("sample.var1")); SSTRING v2 (master_registry.get("sample.var2")); master_registry.end_session(); xconf_notice ("Using the virtual registry, we get\n" "var1=%s\n" "var2=%s\n" "\n" "We will put new values now" ,v1.get(),v2.get()); master_registry.start_session(); master_registry.set("sample.var1","New value for var1"); master_registry.set("sample.var2","New value for var2"); master_registry.end_session(); xconf_notice ("Ok, the values are updated, now we visit the dialog again"); sample_registrydialog(); } // #Specend: // #Specbeg: dialog / sample code / using the tree menu static void sample_tree() { if (dialog_mode != DIALOG_GUI){ xconf_notice ("Only in GUI mode"); }else{ DIALOG dia; dia.gui_passthrough(P_Treemenu,"tree $mode=1"); for (int i=0; i<3; i++){ dia.gui_passthrough (P_Treesub,"1 \"\" dir%d",i); for (int j=0; j<4; j++){ dia.gui_passthrough (P_Treeelem,"\"\" file-%d-%d",i,j); } dia.gui_end(); } dia.gui_end(); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("A tree","",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_OK){ xconf_notice ("ok :%s:",diagui_getlast_actionid()); } } } } // #Specend: // #Specbeg: dialog / sample code / graphical drawing primitives /* This example shows how to use the GUI protocol to produce arbitraty drawings in a window */ static void sample_variousdraw( int offx, int offy, const char *dcstatus) { diagui_sendcmd (P_Drawline,"%d %d %d %d $dc=%s\n",offx,offy,1000+offx,1000+offy,dcstatus); diagui_sendcmd (P_Drawrect,"%d %d %d %d $dc=%s\n",10+offx,1+offy ,30+offx,30+offy,dcstatus); diagui_sendcmd (P_Fillrect,"%d %d 30 70 $dc=%s\n",10+offx,40+offy,dcstatus); diagui_sendcmd (P_Drawarc,"%d %d %d %d %d %d $dc=%s\n" ,50+offx,100+offy,200+offx,100+offy,100+offx,150+offy,dcstatus); } static void sample_drawings() { if (dialog_mode != DIALOG_GUI){ xconf_notice ("Only in GUI mode!"); return; } // We define a drawing context and some pens, fonts and brushes // We see that these definitions are global. Unrelated to any // dialogs (forms) const char *dcwhite; const char *dcstatus; { const char *fontstatus = guiid_setfont (24,GFONT_ID_MODERN,GFONT_STYLE_SLANT,GFONT_WEIGHT_DEFAULT,false); const char *penstatus = guiid_setpen ("blue",1,GPEN_STYLE_SOLID); const char *brushstatus = guiid_setbrush ("blue",GBRUSH_STYLE_SOLID); dcstatus = guiid_setdc (fontstatus,penstatus,brushstatus); const char *penwhite = guiid_setpen ("white"); const char *brushwhite = guiid_setbrush ("white"); dcwhite = guiid_setdc (NULL,penwhite,brushwhite); } // We will create a dialog with two fields and some graphics under DIALOG dia; SSTRING s1,s2; dia.newf_str ("Field1",s1); dia.newline(); dia.newf_str ("Field2",s2); dia.newline(); // Now we create a sub-form and draw in it. // Since the form does not contain anything other object // we have to force the size dia.gui_passthrough (P_Form,"draw $w=200 h=200"); dia.gui_passthrough (P_Clear,"$dc=%s\n",dcwhite); dia.gui_passthrough (P_Drawline,"0 0 1000 1000 $dc=%s\n",dcstatus); dia.gui_passthrough (P_Drawrect,"10 1 30 30 $dc=%s\n",dcstatus); dia.gui_passthrough (P_Fillrect,"10 40 30 70 $dc=%s\n",dcstatus); dia.gui_passthrough (P_Drawarc,"180 10 130 10 150 10 $dc=%s\n",dcstatus); dia.gui_passthrough (P_Drawarc,"50 100 200 100 100 150 $dc=%s\n",dcstatus); dia.gui_end(); dia.gui_dispolast (GUI_H_CENTER,2,GUI_V_TOP,1); int nof = 0; int offx = 0; int offy = 0; dia.setbutinfo (MENU_USR1,"raise","raise"); dia.setbutinfo (MENU_USR2,"right","right"); while (1){ MENU_STATUS code = dia.edit ("Sample drawing","",help_nil ,nof,MENUBUT_USR1|MENUBUT_USR2|MENUBUT_ACCEPT|MENUBUT_CANCEL); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_USR1 || code == MENU_USR2){ SSTRING tmp; dia.setguiname(tmp); diagui_sendcmd (P_Setcontext,"%s.panel.draw\n",tmp.get()); diagui_sendcmd (P_Clear,"$dc=%s\n",dcwhite); if (code == MENU_USR1){ offy--; }else{ offx++; } sample_variousdraw(offx ,offy,dcstatus); diagui_sendcmd (P_Refresh,"\n"); diagui_sendcmd (P_End,"\n"); }else if (code == MENU_ACCEPT){ break; } } } // #Specend: // #Specbeg: dialog / sample code / graphical popup menus /* This shows how to create popup menus */ static void sample_popup(int id) { DIALOG_MENUPOPUP dia; char recordid[15]; sprintf (recordid,"record %d",id); dia.new_menuitem ("create","option1"); dia.new_menuitem ("create","option2"); dia.new_menuitem ("","-"); // A separator dia.newf_title ("icon1",1,"","Sub-menu3"); dia.new_menuitem ("create","option3-1"); dia.new_menuitem ("create","option3-2"); dia.newf_title ("icon2",1,"","Sub-menu4"); dia.new_menuitem ("create","option4-1"); dia.new_menuitem ("create","option4-2"); dia.newf_title ("",""); // Trick to get back to the first level dia.new_menuitem ("create","option5"); dia.new_menuitem ("create","option6"); int nof= 0; while (1){ MENU_STATUS code = dia.editmenu (recordid, nof); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else{ xconf_notice ("You have selected option %d",nof); break; } } } /* Here is a sample dialog presenting records. Clicking on a record brings a popup */ static void sample_usepopup() { DIALOG_RECORDS dia; dia.newf_head ("","Name\tPhone\tZip"); int nof=0; while (1){ for (int i=0; i<5; i++){ char tmp[10]; sprintf (tmp,"user%d",i+1); dia.set_menuitem (i,tmp,"111-2222\txxx-yyy"); } dia.remove_last (5+1); MENU_STATUS code = dia.editmenu ("User list" ,"Click on a record to bring a popup menu" ,help_nil,nof,0); if (code == MENU_ESCAPE || code == MENU_QUIT){ break; }else{ sample_popup(nof); } } } // #Specend: // #Specbeg: dialog / sample code / adding special buttons /* The following code presents two way to add buttons to a dialog. The UI toolkit defines a standard way to place buttons at the bottom of a dialog. Further, it defines standard buttons such as save, cancel, reset and so on. You can add user defined button at the bottom of the dialog as well as user defined buttons anywhere in the dialog. This functionality (adding buttons anywhere) better fit the GUI model. */ static void sample_extrabuttons() { DIALOG dia; SSTRING f1,f2; PRIVATE_MESSAGE m1,m2; int field_f1 = dia.getnb(); // Get the index of this field dia.newf_str ("Field 1",f1); dia.new_button ("reset field 1","some help about this button",m1,true); dia.newline(); int field_f2 = dia.getnb(); // Get the index of this field dia.newf_str ("Field 2",f2); dia.new_button_icon ("xquit","some help about this button",m2); dia.new_button_icon ("qmark","some help about this button",m2); dia.new_button_icon ("uparrow","some help about this button",m2); dia.new_button_icon ("downarrow","some help about this button",m2); dia.newline(); // Adding a "default" button at the bottom // You assign one MENU_USR_ to your button dia.setbutinfo (MENU_USR1,"default","icon-default"); int nof = 0; while (1){ MENU_STATUS code = dia.edit("extrabuttons","",help_nil,nof ,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_ACCEPT){ xconf_notice ("f1=%s\nf2=%s",f1.get(),f2.get()); break; }else if (code == MENU_USR1){ f1.setfrom ("default for field 1"); f2.setfrom ("default for field 2"); dia.reload(); }else if (code == MENU_MESSAGE){ if (dialog_testmessage(m1)){ f1.setfrom (""); dia.reload(field_f1); }else if (dialog_testmessage(m2)){ f2.setfrom (""); dia.reload (field_f2); } } } } // #Specend: // #Specbeg: dialog / sample code / Changing the text or icon of a button /* The following code shows how you can change the label or the icon of a button, on the fly. */ static void sample_changebuttons() { // The following dialog presents a gauge which grow one step every one // or two seconds. Using the little icon, you can stop or start the // animation. Using the text field, you can control the rate. DIALOG dia; int gauge = 0; PRIVATE_MESSAGE m1,m2; dia.newf_gauge ("",gauge,100); FIELD_BUTTON_ICON *button_stop = dia.new_button_icon ("stop" ,"some help about this button",m1); dia.newline(); static const char *button_labels[]={ "Every seconds", "Every two seconds" }; int rate = 0; FIELD_BUTTON_TEXT *button_rate = dia.new_button (button_labels[0] ,"some help about this button",m2); dia.newline(); // We define the timer PRIVATE_MESSAGE timer; dialog_settimer (timer,1,true); dia.waitfor (timer); int nof = 0; bool stopped = false; while (1){ MENU_STATUS code = dia.edit("changebuttons","",help_nil,nof ,MENUBUT_CANCEL); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_MESSAGE){ if (dialog_testmessage(timer)){ gauge = (gauge+5)%100; dia.reload(); }else if (dialog_testmessage(m1)){ if (stopped){ dialog_settimer (timer,rate+1,true); stopped = false; button_stop->seticon ("stop"); }else{ button_stop->seticon ("run"); dialog_deltimer (timer); stopped = true; } }else if (dialog_testmessage(m2)){ rate = (rate+1)%2; button_rate->settext (button_labels[rate]); if (!stopped){ dialog_deltimer (timer); dialog_settimer (timer,rate+1,true); } } } } } // #Specend: // #Specbeg: dialog / sample code / tuning field GUI look /* Using the DIALOG::set_guiparms, you can pass extra GUI parameters. You must know which parameters apply to which widget. This is documented (or will be :-) ) in ../doc/guiapi.sgml. The current example shows how you can affect the look of richtext field */ static void sample_guiparms() { DIALOG_TEXTBOX dia; static const char *dcstatus = NULL; if (dcstatus == NULL){ const char *fontstatus = guiid_setfont (24,GFONT_ID_MODERN,GFONT_STYLE_SLANT,GFONT_WEIGHT_BOLD,false); const char *penstatus = guiid_setpen ("blue"); const char *brushstatus = guiid_setbrush ("blue"); dcstatus = guiid_setdc (fontstatus,penstatus,brushstatus); } dia.newf_text ("","Using blue font"); dia.set_guiparms ("dc=%s",dcstatus); dia.newf_text ("","Using normal font"); dia.newf_text ("",""); dia.edit ("guiparms test","",help_nil); } // #Specend: // #Specbeg: dialog / sample code / GUI fonts /* The following code presents the various fonts available. */ static void sample_guifonts() { DIALOG dia; for (GFONT_ID id=GFONT_ID_DEFAULT; id <= GFONT_ID_TELETYPE; id=(GFONT_ID)(id+1)){ for (GFONT_STYLE style=GFONT_STYLE_DEFAULT; style <= GFONT_STYLE_ITALIC; style=(GFONT_STYLE)(style+1)){ for (GFONT_WEIGHT weight=GFONT_WEIGHT_DEFAULT; weight <= GFONT_WEIGHT_LIGHT; weight=(GFONT_WEIGHT)(weight+1)){ const char *font = guiid_setfont (20,id,style,weight,false); const char *dc = guiid_setdc (font,NULL,NULL); dia.gui_label ("\"text %d %d %d\" $dc=%s",id,style,weight,dc); dia.newline(); } } } int nof = 0; dia.edit("GUI fonts","",help_nil,nof); } // #Specend: // #Specbeg: dialog / sample code / using clist /* Using the DIALOG::newf_clist, one can create dialog with multiple clist and fields as needed. This is unlike the DIALOG_LISTE and DIALOG_RECORDS which are dealing with a single list dialog (no extra fields). At this point, the FIELD_CLIST is only debugged for the GUI mode, but could work in text mode later. It probably won't work in HTML mode. */ static void sample_clist() { DIALOG dia; PRIVATE_MESSAGE msg1; int sel1 = 0; FIELD_CLIST *clist1 = dia.newf_clist ("",10,msg1,sel1); clist1->setheader ("col1\tcol2\tMore"); for (int i=0; i<20; i++){ clist1->setrecordf (i,"liste1\tline%d\textra",i); } SSTRING f; dia.newf_str ("Field1",f); PRIVATE_MESSAGE msg2; int sel2 = 0; FIELD_CLIST *clist2 = dia.newf_clist ("",10,msg2,sel2); clist2->setheader ("col1\tcol2"); for (int i=0; i<20; i++){ clist2->setrecordf (i,"liste2\tline%d",i); } dia.newline(); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("2 clists","",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_MESSAGE){ if (dialog_testmessage (msg1)){ f.setfromf ("liste1,line%d",sel1); }else if (dialog_testmessage (msg2)){ f.setfromf ("liste2,line%d",sel2); }else{ fprintf (stderr,"Unknown message %d %d %d\n",nof,sel1,sel2); } dia.reload(); }else{ f.setfromf ("ok %d",nof); dia.reload(); } } } // #Specend: // #Specbeg: dialog / sample code / sorted clist /* We show how to handle sorted clist as well as handling column attributes */ static void sample_clist_sorted() { DIALOG dia; int sel = 0; PRIVATE_MESSAGE msg; FIELD_CLIST *clist = dia.newf_clist (NULL,10,msg,sel); clist->setheader ("Num.\tTest sequence\tComment"); bool sortdown = true; int sortcol = 0; // Sorted column clist->mayclickhead(); clist->sethsign ("d--"); const char *font1 = guiid_setfont (14,GFONT_ID_DEFAULT,GFONT_STYLE_SLANT,GFONT_WEIGHT_DEFAULT,false); const char *font2 = guiid_setfont (10,GFONT_ID_DEFAULT,GFONT_STYLE_DEFAULT,GFONT_WEIGHT_DEFAULT,false); const char *font3 = guiid_setfont (12,GFONT_ID_DEFAULT,GFONT_STYLE_DEFAULT,GFONT_WEIGHT_BOLD,false); const char *brush1 = guiid_setbrush ("red",GBRUSH_STYLE_SOLID); const char *pen1 = guiid_setpen ("red",1,GPEN_STYLE_SOLID); const char *dc1 = guiid_setdc (font1,pen1,brush1); const char *dc2 = guiid_setdc (font2,NULL,NULL); const char *dc3 = guiid_setdc (font3,NULL,NULL); char dcs[100]; sprintf (dcs,"%s,%s,%s",dc1,dc2,dc3); clist->setdcs (dcs); for (int i=0; i<100; i++){ clist->setnextdcs (i & 1 ? dc1 : NULL); // Inherit default drawing context clist->setrecordf (i,"%d\ttest %d\tcomment %d",i,i,i); } clist->setnextdcs (NULL); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("sorted clist","",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_MESSAGE){ if (dialog_testmessage(msg)){ int column = clist->whichcolumn(); if (column != -1){ if (sortcol == column){ sortdown = !sortdown; }else{ sortdown = true; sortcol = column; } clist->sethsign (column,sortdown ? 'd' : 'u'); // At this point we must redefine the content to // show some different ordering }else{ // On row was selected xconf_notice ("Row %d selected",sel); clist->setrecordf (sel,"%d\ttestxxx %d\tcommentxxx %d",sel,sel,sel); } } }else{ break; } } } // #Specend: // #Specbeg: dialog / sample code / clist management /* We show how to handle various operation on sorted clist. Using the button 1 or 2 or 3 on a clist, you replace, insert or delete a record. */ static void sample_clist_manage() { DIALOG dia; int sel = 0; PRIVATE_MESSAGE msg; FIELD_CLIST *clist = dia.newf_clist (NULL,10,msg,sel); clist->setheader ("Num.\tTest sequence\tComment"); for (int i=0; i<100; i++){ clist->setrecordf (i,"%d\ttest %d\tcomment %d",i,i,i); } int nof = 0; while (1){ MENU_STATUS code = dia.edit ("sorted clist","",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_MESSAGE){ if (dialog_testmessage(msg)){ UISTATE st; diajava_lastmousestate(st); if (st.leftb){ // We replace the record sel clist->setrecordf (sel,"%d\tChanged %d\tNew %d",sel,sel,sel); }else if (st.middleb){ char tmp[100]; sprintf (tmp,"%d\tInserted %d\tIns %d",sel,sel,sel); clist->insrecord (sel,tmp); }else if (st.rightb){ clist->remove (sel); } } }else{ break; } } } // #Specend: // #Specbeg: dialog / sample code / using inputgrid /* The inputgrid facility only works in GUI mode. It allows the application to capture mouse selection in a form. You define a grid and any mouse selection (click) within that grid will be reported like a button using a PRIVATE_MESSAGE. Using the $track=1 extra parameter, one can also follow the mouse movement over a grid. The following code will show both. */ static void sample_inputgrid() { DIALOG dia; dia.set_formparms ("w=400 h=200"); // First we draw two grid. Not useful for the input grid, only // to show what is going on. for (int i=0; i<2; i++){ int startx = i*200+10; int endx = i*200+110; int starty = 10; int endy = 110; for (int j=0; j<6; j++){ int x = startx + j*20; int y = starty + j*20; dia.gui_passthrough (P_Drawline,"%d %d %d %d",x,starty,x,endy); dia.gui_passthrough (P_Drawline,"%d %d %d %d",startx,y,endx,y); } } PRIVATE_MESSAGE msg1,msg2; dia.new_inputgrid (10,10,20,20,5,5,msg1); dia.new_inputgrid (210,10,20,20,5,5,msg2); dia.set_guiparms ("track=1"); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("2 inputgrid","",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_MESSAGE){ UISTATE st; diajava_lastmousestate(st); if (dialog_testmessage (msg1)){ fprintf (stderr,"click in left area, cell %d,%d button %d %d %d ctrl %d shift %d alt %d\n" ,(st.x-10)/20,(st.y-10)/20 ,st.leftb,st.middleb,st.rightb ,st.ctrlkey,st.shiftkey,st.altkey); }else if (dialog_testmessage (msg2)){ if (st.leftb || st.middleb || st.rightb){ fprintf (stderr,"click in right area, cell %d,%d\n" ,(st.x-210)/20,(st.y-10)/20); }else if (st.x < 210 || st.x >= 310 || st.y < 10 || st.y >= 110){ fprintf (stderr,"Leaving right area\n"); }else{ fprintf (stderr,"Entering in right area, cell %d,%d\n" ,(st.x-210)/20,(st.y-10)/20); } } } } } // #Specend: // #Specbeg: dialog / sample code / using request_dump /* This is specific to GUI mode. In general, dialog operates in standalone mode until the user hit one of their buttons. At this point, the form content is sent to the application. Sometime, you have two independant dialogs tied logically. An action in one may request some test on the current content of the fields in the other dialog. The only way to know the current content of those fields is to request the GUI front-end to perform a dump of the dialog, even if the user has not hit any button. The following example present a small text editor with a companion control dialog. Each time one hit the "add" button in the control dialog, one line is added to the text editor. Yet, the current content of the text has to be preserved. */ struct EDIT_INFO{ PRIVATE_MESSAGE msg; SSTRING line; }; static void sample_texteditor(void *data) { EDIT_INFO *info = (EDIT_INFO*)data; DIALOG dia; dia.waitfor (info->msg); SSTRING txt; dia.newf_textarea (NULL,txt,70,25); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("Small editor" ,"Type something here\n" "Do not hit any button in this dialog\n" "Then hit a button in the editor control" ,help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_DUMP){ dia.save(); txt.appendf ("%s\n",info->line.get()); dia.reload(); }else if (code == MENU_MESSAGE){ if (dialog_testmessage(info->msg)){ dia.request_dump(); } }else if (code == MENU_ACCEPT){ xconf_notice ("The text is now\n%s",txt.get()); } } } static void sample_dump() { if (dialog_mode != DIALOG_GUI){ xconf_notice ("Only in GUI mode!"); return; } EDIT_INFO info; uithread (sample_texteditor,&info); DIALOG dia; dia.newf_str ("New text line",info.line); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("Editor control","",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_ACCEPT){ dialog_sendmessage (info.msg); } } } // #Specend: // #Specbeg: dialog / sample code / following notebook page focus /* This is specific to GUI mode. In complex dialog, we may want to map several independant dialogs (documents) in a notebook. It might be important to know which document has the visibility focus so we can direct some action properly. For example, if we have a pull-down menu and use the save menu option, we have to know which one will be saved. Note that this kind of application are better handled with the TLMP framework object... The page focus concept only inform about the page number currently shown. We have to translate this so we know which document is concerned. */ struct PAGE_INFO{ SSTRING path; PRIVATE_MESSAGE reload; PRIVATE_MESSAGE quit; PRIVATE_MESSAGE save; PAGE_INFO(const char *_path){ path.setfrom (_path); } }; static void sample_pagedocument(void *data) { PAGE_INFO *info = (PAGE_INFO*)data; DIALOG dia; dia.waitfor (info->reload); dia.waitfor (info->quit); dia.waitfor (info->save); dia.setcontext ("main.book"); SSTRING txt; dia.newf_textarea (NULL,txt,40,5); int nof = 0; while (1){ MENU_STATUS code = dia.edit (info->path.get(),"",help_nil,nof,0); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_MESSAGE){ if (dialog_testmessage (info->quit)){ delete info; break; }else if (dialog_testmessage (info->reload)){ txt.setfromf ("This is Document %s\n",info->path.get()); dia.reload(); }else if (dialog_testmessage(info->save)){ xconf_notice ("Saving %s\n",info->path.get()); } } } } static void sample_adddocument ( PAGE_INFO *info[], int &nbdocument, int &alloc) { char tmp[100]; sprintf (tmp,"/tmp/document-%d",alloc++); PAGE_INFO *page = new PAGE_INFO (tmp); info[nbdocument++] = page; uithread (sample_pagedocument,page); } static void sample_pagefocus() { diagui_sendcmd (P_MainForm,"main\n"); // Create a small menu bar diagui_sendcmd (P_Menubar,"\n"); diagui_sendcmd (P_Submenu,"File\n"); diagui_sendcmd (P_Menuentry,"1 open\n"); diagui_sendcmd (P_Menuentry,"2 reload\n"); diagui_sendcmd (P_Menuentry,"3 save\n"); diagui_sendcmd (P_Menuentry,"4 close document\n"); diagui_sendcmd (P_Menuentry,"5 quit\n"); diagui_sendcmd (P_End,"\n"); diagui_sendcmd (P_End,"\n"); diagui_sendcmd (P_Book,"book $focus=B200\n"); diagui_sendcmd (P_End,"\n"); // This is the array which will link page number to document PAGE_INFO *info[10]; int nbdocument = 0; int alloc = 0; // Use to format the document name /tmp/document-%d // Create 3 dummy documents sample_adddocument (info,nbdocument,alloc); sample_adddocument (info,nbdocument,alloc); sample_adddocument (info,nbdocument,alloc); diagui_sendcmd (P_End,"\n"); int document = 0; while (1){ SSTRING path,action,menu; int button = diagui_sync ("main",path,action,menu); // fprintf (stderr,"sync %d :%s: :%s: :%s:\n",button,path.get(),action.get(),menu.get()); if (menu.is_filled()){ int id = menu.getval(); if (id == 1){ sample_adddocument (info,nbdocument,alloc); }else if (id == 2){ dialog_sendmessage (info[document]->reload); }else if (id == 3){ dialog_sendmessage (info[document]->save); }else if (id == 4){ if (document < nbdocument){ dialog_sendmessage (info[document]->quit); // We forget the document memcpy (info+document,info+document+1 ,(10-document-1)*sizeof(PAGE_INFO*)); nbdocument--; } }else if (id == 5){ break; } }else if (button == 200){ document = atoi(diajava_getextrareport()); fprintf (stderr,"The page having focus is now %d\n",document); } } diagui_sendcmd (P_Delete,"main\n"); diagui_flush(); } // #Specend: // #Specbeg: dialog / sample code / changing dialog intro /* It is possible to change the dialog intro section from one call of DIALOG::edit to the other. Here is a sample code doing this */ static void sample_changeintro() { DIALOG dia; SSTRING intro; intro.setfrom ("This is the intro"); dia.setbutinfo (MENU_USR1,"reset","reset"); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("change intro",intro.get(),help_nil ,nof ,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_USR1){ intro.setempty(); }else if (code == MENU_ACCEPT){ intro.append ("\nOne more line"); } } } // #Specend: // #Specbeg: dialog / sample code / inserting fields /* It is possible to insert new fields in a running dialog. */ static void sample_insert_fields() { DIALOG dia; SSTRING f1,f2,f3,other; dia.newf_str ("Field 1",f1); dia.newf_str ("Field 2",f2); dia.newf_title ("","new fields go here"); int insert_pos = dia.getnb(); int original_pos = insert_pos; dia.newf_title ("","Other fields"); dia.newf_str ("last field",f3); dia.setbutinfo (MENU_USR1,"more fields","more fields"); dia.setbutinfo (MENU_USR2,"remove fields","remove fields"); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("insert fields" ,"You can insert and remove fields",help_nil ,nof ,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1|MENUBUT_USR2); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_USR1){ dia.set_nextfield (insert_pos++); // We are using the same edit variable, this is a sample only dia.newf_str ("Another field",other); }else if (code == MENU_USR2){ if (insert_pos > original_pos){ dia.remove_del (--insert_pos); } }else if (code == MENU_ACCEPT){ break; } } } // #Specend: // #Specbeg: dialog / sample code / long string /* By default, strings are limited to 200 characters. This is not a limitation of the DIALOG class, but an hardcoded value in the SSTRING class. This is changed using the SSTRING::setmaxsiz. The idea is that any code in linuxconf not expecting a very long input string, won't get one. The maxsiz feature of the SSTRING is only enforced there. Doing a SSTRING::setfrom("a very very long string") works. It just in this case it does not. Here is some sample code showing how you can control this. There is two fields, one limited, one not. Note the that GUI protocol itself will is limited to 1000 characters by line. */ static void sample_longstring() { DIALOG dia; SSTRING normal,longone; longone.setmaxsiz (500); dia.newf_str ("Normal string",normal); dia.newf_str ("Long one",longone); int nof = 0; while (1){ MENU_STATUS code = dia.edit ("long string","",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ printf ("normal is %d long\n",normal.getlen()); printf ("longone is %d long\n",longone.getlen()); } } } // #Specend: // #Specbeg: dialog / sample code / FIELD_HTML /* We can display HTML content in GUI mode (for now) using the FIELD_HTML and DIALOG::newf_html. The current sample shows how we display an HTML file and then interact with the browser to validate/redirect URLs. */ static void sample_html (const char *file) { if (!diajava_html){ xconf_notice ("The current GUI front-end does not handle HTML"); }else{ DIALOG dia; dia.set_formparms ("vtrigger=600"); SSTRING s; dia.newf_str ("text",s); dia.newf_str ("text",s); dia.newf_str ("text",s); dia.newf_str ("text",s); dia.newf_str ("text",s); dia.newf_str ("text",s); dia.newf_str ("text",s); FIELD_HTML *ht = dia.newf_file_html ("HTML",60,25,file); int nof = 0; while (1){ MENU_STATUS code = dia.edit (file,"introduction",help_nil,nof); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else{ break; } } } } // #Specend: int main (int argc, char *argv[]) { argc = dialog_parseuioptions (argc, argv); linuxconf_loadmsg ("linuxconf",PACKAGE_REV); translat_checkmissing(); dialog_clear(); if (argc == 2 && strcmp(argv[1],"records")==0){ sample_records(); }else if (argc == 2 && strcmp(argv[1],"messages")==0){ sample_messages(); }else if (argc == 2 && strcmp(argv[1],"timers")==0){ sample_timer(); }else if (argc == 2 && strcmp(argv[1],"privates")==0){ sample_privates(); }else if (argc == 2 && strcmp(argv[1],"reload")==0){ sample_reload(); }else if (argc == 2 && strcmp(argv[1],"thread")==0){ sample_thread(); }else if (argc == 2 && strcmp(argv[1],"threaddata")==0){ sample_threaddata(); }else if (argc == 2 && strcmp(argv[1],"helplist")==0){ sample_helplist(); }else if (argc == 2 && strcmp(argv[1],"helpdialog")==0){ sample_helpdialog(); }else if (argc == 2 && strcmp(argv[1],"helpcontext")==0){ sample_helpcontext(); }else if (argc == 2 && strcmp(argv[1],"tricks")==0){ sample_dialogtricks(); }else if (argc == 2 && strcmp(argv[1],"chkmvals")==0){ sample_chkmvals(); }else if (argc == 2 && strcmp(argv[1],"textarea")==0){ sample_textarea(); }else if (argc == 2 && strcmp(argv[1],"textarea-mixed")==0){ sample_textarea_mixed(); }else if (argc == 2 && strcmp(argv[1],"guipassthrough")==0){ sample_guipassthrough(); }else if (argc == 2 && strcmp(argv[1],"notebook")==0){ sample_notebook(); }else if (argc == 2 && strcmp(argv[1],"vregistry")==0){ sample_useregistry(); }else if (argc == 2 && strcmp(argv[1],"tree")==0){ sample_tree(); }else if (argc == 2 && strcmp(argv[1],"drawings")==0){ sample_drawings(); }else if (argc == 2 && strcmp(argv[1],"popup")==0){ sample_usepopup(); }else if (argc == 2 && strcmp(argv[1],"extrabuttons")==0){ sample_extrabuttons(); }else if (argc == 2 && strcmp(argv[1],"changebuttons")==0){ sample_changebuttons(); }else if (argc == 2 && strcmp(argv[1],"guiparms")==0){ sample_guiparms(); }else if (argc == 2 && strcmp(argv[1],"clist")==0){ sample_clist(); }else if (argc == 2 && strcmp(argv[1],"clist-sorted")==0){ sample_clist_sorted(); }else if (argc == 2 && strcmp(argv[1],"clist-manage")==0){ sample_clist_manage(); }else if (argc == 2 && strcmp(argv[1],"guifonts")==0){ sample_guifonts(); }else if (argc == 2 && strcmp(argv[1],"inputgrid")==0){ sample_inputgrid(); }else if (argc == 2 && strcmp(argv[1],"dump")==0){ sample_dump(); }else if (argc == 2 && strcmp(argv[1],"pagefocus")==0){ sample_pagefocus(); }else if (argc == 2 && strcmp(argv[1],"autonewline")==0){ sample_autonewline(); }else if (argc == 2 && strcmp(argv[1],"changeintro")==0){ sample_changeintro(); }else if (argc == 2 && strcmp(argv[1],"insert_fields")==0){ sample_insert_fields(); }else if (argc == 2 && strcmp(argv[1],"longstring")==0){ sample_longstring(); }else if (argc == 3 && strcmp(argv[1],"html")==0){ sample_html(argv[2]); }else{ fprintf (stderr,"/tmp/x autonewline\n"); fprintf (stderr,"/tmp/x changebuttons\n"); fprintf (stderr,"/tmp/x changeintro\n"); fprintf (stderr,"/tmp/x chkmvals\n"); fprintf (stderr,"/tmp/x clist\n"); fprintf (stderr,"/tmp/x clist-sorted\n"); fprintf (stderr,"/tmp/x clist-manage\n"); fprintf (stderr,"/tmp/x drawings\n"); fprintf (stderr,"/tmp/x dump\n"); fprintf (stderr,"/tmp/x extrabuttons\n"); fprintf (stderr,"/tmp/x guifonts\n"); fprintf (stderr,"/tmp/x guiparms\n"); fprintf (stderr,"/tmp/x guipassthrough\n"); fprintf (stderr,"/tmp/x helpdialog\n"); fprintf (stderr,"/tmp/x helplist\n"); fprintf (stderr,"/tmp/x helpcontext\n"); fprintf (stderr,"/tmp/x html file.html\n"); fprintf (stderr,"/tmp/x inputgrid\n"); fprintf (stderr,"/tmp/x insert_fields\n"); fprintf (stderr,"/tmp/x longstring\n"); fprintf (stderr,"/tmp/x messages\n"); fprintf (stderr,"/tmp/x notebook\n"); fprintf (stderr,"/tmp/x pagefocus\n"); fprintf (stderr,"/tmp/x popup\n"); fprintf (stderr,"/tmp/x privates\n"); fprintf (stderr,"/tmp/x records\n"); fprintf (stderr,"/tmp/x reload\n"); fprintf (stderr,"/tmp/x textarea\n"); fprintf (stderr,"/tmp/x textarea-mixed\n"); fprintf (stderr,"/tmp/x thread\n"); fprintf (stderr,"/tmp/x threaddata\n"); fprintf (stderr,"/tmp/x timers\n"); fprintf (stderr,"/tmp/x tree\n"); fprintf (stderr,"/tmp/x tricks\n"); fprintf (stderr,"/tmp/x vregistry\n"); } return 0; }