Populating Reaction Listbrowsers from a file?

11 posts / 0 new
Last post
AmigaOneFan
AmigaOneFan's picture
Offline
Last seen: 10 years 3 months ago
Joined: 2011-11-14 16:18
Populating Reaction Listbrowsers from a file?

Hi,

I have a function called make_browserlist as follows:

  1. BOOL make_browserlist(struct List *list, char **tagstring, int columns)
  2. {
  3. struct Node *node;
  4.  
  5. IExec->NewList(list);
  6.  
  7. switch (columns)
  8. {
  9. case 2:
  10. while (*tagstring)
  11. {
  12. if (node = IListBrowser->AllocListBrowserNode(columns,
  13. LBNA_Column, 0,
  14. LBNCA_Text, *tagstring++,
  15. LBNA_Column, 1,
  16. LBNCA_Text, *tagstring++,
  17. TAG_DONE))
  18. {
  19. IExec->AddTail(list, node);
  20. }
  21. else
  22. {
  23. IDOS->Printf(" AllocListBrowserNode() failed\n");
  24. return(FALSE);
  25. }
  26. }
  27.  
  28. case 3:
  29. while (*tagstring)
  30. {
  31. if (node = IListBrowser->AllocListBrowserNode(columns,
  32. LBNA_Column, 0,
  33. LBNCA_Text, *tagstring++,
  34. LBNA_Column, 1,
  35. LBNCA_Text, *tagstring++,
  36. LBNA_Column, 2,
  37. LBNCA_Text, *tagstring++,
  38. TAG_DONE))
  39. {
  40. IExec->AddTail(list, node);
  41. }
  42. else
  43. {
  44. IDOS->Printf(" AllocListBrowserNode() failed\n");
  45. return(FALSE);
  46. }
  47. }
  48.  
  49. case 5:
  50. while (*tagstring)
  51. {
  52. if (node = IListBrowser->AllocListBrowserNode(columns,
  53. LBNA_Column, 0,
  54. LBNCA_Text, *tagstring++,
  55. LBNA_Column, 1,
  56. LBNCA_Text, *tagstring++,
  57. LBNA_Column, 2,
  58. LBNCA_Text, *tagstring++,
  59. LBNA_Column, 3,
  60. LBNCA_Text, *tagstring++,
  61. LBNA_Column, 4,
  62. LBNCA_Text, *tagstring++,
  63. TAG_DONE))
  64. {
  65. IExec->AddTail(list, node);
  66. }
  67. else
  68. {
  69. IDOS->Printf(" AllocListBrowserNode() failed\n");
  70. return(FALSE);
  71. }
  72. }
  73.  
  74. break;
  75.  
  76. default:
  77. IDOS->Printf(" Invalid number of list fields\n");
  78. return(FALSE);
  79.  
  80. }; // switch
  81.  
  82. return(TRUE);
  83. }

I can call this with static STRPTR string data and populate my listbrowsers fine.
However, I want to use char[] arrays that have strings I have retrieved from an input text file to populate my listbrowser lists using this same function.
I suspect I need to coerce my character arrays into static strings somehow. When I try to pass my char arrays into this function I always seem to get DSI errors no matter what I do. So for example I might do the following:

  1. char mylist[1000];
  2. ...
  3. if(make_browserlist(&listbrowser_list1, mylist, 3)
  4. {
  5. // list made okay - continue with program
  6. }
  7. else
  8. {
  9. IDOS-Printf("Error making browser list!\n");
  10. }

I am retrieving mylist using basic C fileio and verifying that the string data is actually populated in mylist.
Does mylist have to be global? I have tried calling make_browserlist with (char *)&mylist etc but always get the same result.

Thanks!

Rigo
Rigo's picture
Offline
Last seen: 1 year 10 months ago
Joined: 2011-01-24 21:55
As you say, the file I/O

As you say, the file I/O routines do not return arrays of string pointers, they fill a user buffer with characters until EoF or CR.

You will need to take this buffer and manually copy each word in it into an array of STRPTRs in order to pass it to your function.

Alternatively, you can use FGetC() to retrieve one character from the file at a time and copy these while parsing for white space and newlines.

Simon

AmigaOneFan
AmigaOneFan's picture
Offline
Last seen: 10 years 3 months ago
Joined: 2011-11-14 16:18
You will need to take this

<*>
You will need to take this buffer and manually copy each word in it into an array of STRPTRs in order to pass it to your function.

Yes...painful!
From what I can glean from playing with the GCC debugger, it looks like a STRPTR type is an array of pointers to static strings, each null (byte) terminated. So, for example:

  1. STRPTR PageLabels[] =
  2. {
  3. "Credit or Bank Cards",
  4. " Internet Passwords ",
  5. " Other Passwords ",
  6. " Insurance Policies ",
  7. NULL
  8. };

really shows a series of four addresses, each of which I believe points to a static string in memory. Thus PageLabels[0] = 0x12345678 (an address in memory), which points to the null terminated static string "Credit or Bank Cards".

So is there an easier way to populate list browser strings from getline() file I/O?

Thanks

Rigo
Rigo's picture
Offline
Last seen: 1 year 10 months ago
Joined: 2011-01-24 21:55
It really all depends on how

It really all depends on how your file is structured.

For example, an xml file would make it easier to read in the strings and place them in a list, which could then be used to create the listbrowser nodes.

To be honest, this conversation is far too general to try and offer any solutions. Specific problems are much easier to offer assistance with.

Simon

trixie
trixie's picture
Offline
Last seen: 5 months 14 hours ago
Joined: 2011-02-03 13:58
@ AmigaOneFan Not that it

@ AmigaOneFan

Not that it answers your questions but anyway: I've found it easier and safer to use "LBNCA_CopyText, TRUE" when populating a listbrowser list from string pointers.

AmigaOne X5000-020 / 2GB RAM / Sapphire Pulse Radeon RX 560 / AmigaOS 4.1 Final Edition Update 2

AmigaOneFan
AmigaOneFan's picture
Offline
Last seen: 10 years 3 months ago
Joined: 2011-11-14 16:18
Hi Simon, Yes, I haven't

Hi Simon,

Yes, I haven't given a lot of specifics. Thankfully, I have found a solution, not really elegant but it works! You can find my updated source code here: http://www.os4coding.net/source/162

Since we are using arrays of string pointers, here is a way I populate the data from getline file I/O:

  1. void LoadFile()
  2. {
  3. FILE *Infile;
  4. u_char length1,length2,length3,length4;
  5. char mystring[100];
  6.  
  7. int i,j;
  8.  
  9. Infile = fopen("AmiSafe.dat", "r");
  10. if(Infile == 0)
  11. {
  12. MessageBox("UhOh!","Unable to open AmiSafe.dat input file!");
  13. }
  14. else // read in parameter data
  15. {
  16.  
  17. // Read Tab #1 Info
  18. length1 = fgetc(Infile);
  19. #ifdef DEBUG
  20. IDOS->Printf("Tab 1 has %lu entries\n",length1);
  21. #endif
  22. IExec->NewList((struct List *)&listbrowser_list1);
  23.  
  24. i = 0;
  25. while(length1 > 0)
  26. {
  27. j = COLSTAB1;
  28. while(j > 0)
  29. {
  30. fgets(mystring, 100, Infile);
  31. mystring[strlen(mystring)-1] = 0; // convert \n to 0
  32. strcpy(Tab1Strings[i++], mystring);
  33. j--;
  34. }
  35. length1--;
  36. }
  37. Tab1Strings[i] = 0; // add tail null
  38. ...

I get the string and put it into mystring temporarily, convert the trailing \n into a nul, then copy it to Tab1Strings, which is defined as follows:

  1. #define STRSIZE 40
  2. #define MAXENTRIES 100
  3. #define COLSTAB1 5
  4. #define COLSTAB2 3
  5. #define COLSTAB3 2
  6. #define COLSTAB4 3
  7.  
  8. // Arrays of text data for each tab
  9. STRPTR Tab1Strings[MAXENTRIES];
  10. STRPTR Tab2Strings[MAXENTRIES];
  11. STRPTR Tab3Strings[MAXENTRIES];
  12. STRPTR Tab4Strings[MAXENTRIES];

It is VITALLY IMPORTANT to be sure to malloc the memory I intend to use to store the strings, which I do in my main function prior to calling the LoadFile function that populates the strings:

  1. // Allocate Space for Strings + 1 for nul for all tabs
  2. for(i = 0; i < MAXENTRIES; i++)
  3. {
  4. Tab1Strings[i] = malloc((STRSIZE + 1)*sizeof(char));
  5. Tab2Strings[i] = malloc((STRSIZE + 1)*sizeof(char));
  6. Tab3Strings[i] = malloc((STRSIZE + 1)*sizeof(char));
  7. Tab4Strings[i] = malloc((STRSIZE + 1)*sizeof(char));
  8. }

I also am sure to free this memory upon closing the program. I could simply allocate the memory for each string as I use it and save a few thousand bytes of RAM, but this is a quick and dirty way to ensure I have all the space I need without worrying about malloc and free for every string populate and delete I will do.

So my AmiSafe.c program right now loads in a file of data, populates the list browsers for the four tabs of information from this file data, allows the user to edit in place the content of the strings, then (if you select the HELP button, for now) writes out the data in a format usable by the program in the amisafe.dat file.
Now all I need to do is come up with a way to add a new entry, delete a selected entry, and then add a 4x4 button matrix at startup to allow the user to create and use a password, then incorporate the RIJNDAEL algorithm to encode and decode the file data.
Easy.....I hope!

I hope this source and program helps others who want to do similar things in REACTION listbrowsers.

AmigaOneFan
AmigaOneFan's picture
Offline
Last seen: 10 years 3 months ago
Joined: 2011-11-14 16:18
Okay, now a slightly

Okay, now a slightly different subject....

I am adding new data to a listbrowser by creating a new node and calling AddTail.
How do I update the display and the listbrowser attributes for this new entry? I'm sure there is some simple way to do this.....

Thanks!

Rigo
Rigo's picture
Offline
Last seen: 1 year 10 months ago
Joined: 2011-01-24 21:55
To make any changes to the

To make any changes to the list currently displayed, you first have to detach the list by calling

  1. SetAttrs( listbrowserobj, LISTBROWSER_Labels, NULL, TAG_DONE );

Then you can make your changes (adding, removing or changing attributes). Once that is done, you reattach the list. Depending on how you do this depends on whether the list gets updated visually.
If you use SetAttrs() you will need to manually referesh the gadget afterwards. Luckily, the list can be set like so:

  1. SetGadgetAttrs( listbrowserobj, window, NULL, LISTBROWSER_Labels, labelist, TAG_DONE );

and this will refresh the gadget for you. You might want to check out the listbrowser examples in the Examples directory of the SDK, all this is covered quite nicely.

Simon

AmigaOneFan
AmigaOneFan's picture
Offline
Last seen: 10 years 3 months ago
Joined: 2011-11-14 16:18
Hmmm... I manage to

Hmmm...

I manage to deadlock the system (I think) when I do this. Here's what I am doing, please feel free to point out my foolish mistakes!

  1. // Detach the listbrowser list first
  2. IIntuition->SetAttrs((struct Gadget*)OBJ(OBJ_LISTBROWSER1),
  3. LISTBROWSER_Labels, NULL, TAG_DONE);
  4. ...
  5. change my attributes and add to the end of my labels browserlist here...
  6. ...
  7.  
  8. /* Using RefreshSetGadgetAttrs() is required to force intuition
  9. * to refresh the gadget imagery.
  10. */
  11. IIntuition->RefreshSetGadgetAttrs((struct Gadget*)OBJ(OBJ_LISTBROWSER1),
  12. window, NULL,
  13. LISTBROWSER_Labels, &browserlist,
  14. TAG_DONE);

What am I doing wrong?

Rigo
Rigo's picture
Offline
Last seen: 1 year 10 months ago
Joined: 2011-01-24 21:55
It can only be an invalid

It can only be an invalid pointer, everything else looks fine (what there is of it).

Simon

trixie
trixie's picture
Offline
Last seen: 5 months 14 hours ago
Joined: 2011-02-03 13:58
@AmigaOneFan Your use of

@AmigaOneFan

Your use of type casts looks a little awkward there. OBJ(OBJ_LISTBROWSER1) should have been declared as a pointer to Object so you don't need to cast it to struct Gadget* in SetAttrs(). The correct line would be:

IIntuition->SetAttrs(OBJ(OBJ_LISTBROWSER1), LISTBROWSER_Labels, NULL, TAG_DONE);

SetGadgetAttrs() and RefreshSetGadgetAttrs(), on the other hand, expect their first argument to be a struct Gadget pointer (instead of an Object pointer) so the type cast is appropriate.

In your particular example, not much harm will be done but be careful about unnecessary type casting, it may cause trouble elsewhere.

AmigaOne X5000-020 / 2GB RAM / Sapphire Pulse Radeon RX 560 / AmigaOS 4.1 Final Edition Update 2

Log in or register to post comments