GetFile gadget and long path names

GetFile gadget, long path names and how to deal with them NOTE: A working example of how things look like is uploaded to OS4Depot under the name of 'longpathnames.lha'. At one time we have probably all had to deal with path names that were considerably longer then the width of the getfile gadget. To ensure to have selected the proper file (or directory) a complete visual scan of the contents of the gadget was then the only option. That string had to be interpreted on the fly for the components it was made of. In critical situations, where only one component differed, this could easily lead to some degree of disaster. This has kept me busy for some time now and I think I have found a neat solution to this problem enhancing the userfriendlyness of a GUI. That GUI need no longer be unwieldly wide to accomodate for a wide getfile gadget, which is plain ugly and very distracting. In stead there is only the need for a small gadget wide enough to hold the last part of the pathname string. This latter requires the following tags be in effect for the gadget:
  1. GETFILE_ReadOnly, TRUE,
  2. GETFILE_FilePartOnly, TRUE,
The first one is required for the latter to be effective. The latter ensures that only the last part of a pathname sting is shown, requiring only a small gadget. When the gadget is activated by clicking its button, the IDCMP-loop kicks in:
  1. case WMHI_GADGETUP:
  2. {
  3. switch (WMHI_Message & WMHI_GADGETMASK)
  4. {
  5. case GOID_FILENAME:
  6. {
  7. if (IIntuition->IDoMethod(Obj, GFILE_REQUEST, Win))
  8. {
  9. if ((IIntuition->GetAttrs(Obj, GETFILE_FullFile, (ULONG *)&File, TAG_DONE) == 1))
  10. {
  11. IExec->FreeVec(Hint);
  12.  
  13. if (File)
  14. {
  15. IDOS->Printf("INFO : A file was selected\n");
  16. Hint = Convert_Path2HintInfo(File, Font);
  17. }
  18. else
  19. {
  20. IDOS->Printf("INFO : NO file was selected\n");
  21. Hint = NULL; // = "NO file selected";
  22. }
  23.  
  24. IIntuition->SetGadgetAttrs((struct Gadget *)Obj, Win, NULL, GA_HintInfo, Hint, TAG_END);
  25. }
  26. }
  27.  
  28. if (File)
  29. {
  30. /*
  31.   ** Do your further processing here
  32.   */
  33. }
  34.  
  35. break;
  36. }
  37. }
  38.  
  39. break;
  40. }
In this excerpt there is a call to function Convert_Path2HintInfo(), taking 2 arguments: File and Font. The first is a pointer to the pathname string, the latter is a pointer to a font-and-size string, which will be addressed later. The function Convert_Path2HintInfo() looks like this:
  1. STRPTR Convert_Path2HintInfo(STRPTR File, STRPTR Font)
  2. {
  3. STRPTR Hint = NULL,
  4.  
  5. F = File,
  6. H;
  7.  
  8. uint16 SlashCount = 0,
  9. Skip = 0;
  10.  
  11.  
  12. /*
  13.   ** How many characters form
  14.   ** the volumename-part?
  15.   */
  16. while (*F && (*F != ':'))
  17. {
  18. F++;
  19. }
  20.  
  21. /*
  22.   ** We have now determined where the
  23.   ** volumename part ends and can now
  24.   ** calculate the number of positions
  25.   ** to skip int the upcoming Hint string:
  26.   */
  27. Skip = F - File + 1;
  28.  
  29. /*
  30.   ** Start from the beginning again:
  31.   ** Strange? No, there might have
  32.   ** been no ':' in the string...
  33.   */
  34. F = File;
  35.  
  36. /*
  37.   ** How many slashes are
  38.   ** there in the path?
  39.   */
  40. while (*F)
  41. {
  42. if (*F == '/')
  43. {
  44. SlashCount++;
  45. }
  46.  
  47. F++;
  48. }
  49.  
  50. /*
  51.   ** Now allocate sufficient space for the
  52.   ** Hint string to be built in.
  53.   ** The allocation is cleared with spaces,
  54.   ** which is crucial as during the filling
  55.   ** process positions will be skipped. These
  56.   ** skips are equal in length as the volumename
  57.   ** portion of the pathname:
  58.   ** ( The 1 extra position is for the termination
  59.   ** character, but you would have figured this
  60.   ** out yourself, wouldn't you?)
  61.   */
  62. Hint = IExec->AllocVecTags(strlen(File) + (Skip * SlashCount) + strlen(Font) + 1, AVT_ClearWithValue, 32, TAG_END);
  63.  
  64. if (Hint)
  65. {
  66. H = Hint;
  67.  
  68. /*
  69.   ** Fill with the escape sequence
  70.   ** for the start of a font string:
  71.   */
  72. *H++ = 27;
  73. *H++ = 'f';
  74. *H++ = '[';
  75.  
  76. /*
  77.   ** Now copy the font string
  78.   ** into the HintInfo:
  79.   */
  80. strcpy(H, Font);
  81.  
  82. /*
  83.   ** And set the starting point for
  84.   ** further filling to the position
  85.   ** of the terminating 0:
  86.   */
  87. H = Hint + strlen(Hint);
  88.  
  89. /*
  90.   ** Fill with the escape sequence
  91.   ** for the end of a font string
  92.   ** AND the start of bold text:
  93.   */
  94. *H++ = ']';
  95. *H++ = 27;
  96. *H++ = 'b';
  97.  
  98. /*
  99.   ** The volumename-part
  100.   ** is copied first:
  101.   */
  102. F = File;
  103.  
  104. while (*F && (*F != ':'))
  105. {
  106. *H++ = *F++;
  107. }
  108.  
  109. /*
  110.   ** A 'back-to-normal' escape
  111.   ** sequence is added to the string:
  112.   ** (while we stopped at the ':' character,
  113.   ** this one has to be included first as
  114.   ** it is part of the volumename)
  115.   */
  116.  
  117. *H++ = *F++;
  118. *H++ = 27;
  119. *H++ = 'n';
  120.  
  121. while (*F)
  122. {
  123. /*
  124.   ** Watch out for slashes!
  125.   ** They denote the start of another
  126.   ** component in the pathname string:
  127.   */
  128. if (*F == '/')
  129. {
  130. /*
  131.   ** Add a NewLine (0x0A (= 10))
  132.   ** and subsequently skip a number of
  133.   ** positions in the Hint string:
  134.   */
  135. *H = 10;
  136. H += Skip;
  137. }
  138.  
  139. *H++ = *F++;
  140. }
  141.  
  142. /*
  143.   ** Terminate the Hint
  144.   ** string with a 0:
  145.   */
  146. *H = 0;
  147. }
  148.  
  149. return Hint;
  150. }
More about the escape codes can be read in the AutoDocs: requester_cl.doc. The octal \33 as specified there is 27 in decimal as used here (or 0x1B in hexadecimal) and denotes the start of an escaped sequence. The font-and-size string used is something in this vein: "DejaVu Sans Mono.font/14". This will work perfectly well, but will ignore user's font preferences. This can be overcome by retrieving the system font for drawers as selected in the font preferences editor. This entry selects monospaced fonts only and is thereby perfectly suitable for our purposes. NOTE: be sure to specify a monospace font or the results will look even more ugly then what we tryed to address! The following function will supply you with a pointer to a struct TextAttr. It's argument is the specification to look for, in this case the value 1 will return the entry for the font used to show a drawer's contents.
  1. struct TextAttr *Retrieve_FontPrefs(uint16 Entry)
  2. {
  3. struct TextAttr *ta = ALLOCATE_STRUCT(struct TextAttr);
  4.  
  5. if (ta)
  6. {
  7. struct IFFHandle *iff = IIFF->AllocIFF();
  8.  
  9. if (iff)
  10. {
  11. if ((iff->iff_Stream = (LONG)IDOS->Open("ENVARC:Sys/font.prefs", MODE_OLDFILE)))
  12. {
  13. IIFF->InitIFFasDOS(iff);
  14.  
  15. LONG Error = IIFF->OpenIFF(iff, IFFF_READ);
  16.  
  17. if (Error == IFFERR_NOERROR)
  18. {
  19. if (IIFF->StopChunk(iff, ID_PREF, ID_FONT) == IFFERR_NOERROR)
  20. {
  21. BOOL Done = FALSE;
  22. struct ContextNode *cn;
  23.  
  24. while (((Error = IIFF->ParseIFF(iff, IFFPARSE_SCAN)) == IFFERR_NOERROR) && !Done)
  25. {
  26. if ((cn = IIFF->CurrentChunk(iff)))
  27. {
  28. char FONT_Buff[cn->cn_Size];
  29.  
  30. IIFF->ReadChunkBytes(iff, FONT_Buff, cn->cn_Size);
  31.  
  32. if (((*(uint16 *)(FONT_Buff+14)) == Entry))
  33. {
  34. ALLOCOPY_STRING(ta->ta_Name, ( (STRPTR )(FONT_Buff+28)));
  35. ta->ta_YSize = (*(uint16 *)(FONT_Buff+24));
  36.  
  37. Done = TRUE;
  38. }
  39. }
  40. }
  41. }
  42. ELSE_ERROR("Prop Chunk");
  43.  
  44. IIFF->CloseIFF(iff);
  45. }
  46. ELSE_ERROR("Open IFF");
  47.  
  48. IDOS->Close((BPTR)iff->iff_Stream);
  49. }
  50. ELSE_ERROR("Open");
  51.  
  52. IIFF->FreeIFF(iff);
  53. }
  54. }
  55.  
  56. return ta;
  57. }
Font-and-size string can now easily be created by using a function from the Utility library: FontString = IUtility->ASPrintf("%s/%lu", ta->ta_Name, (ta->ta_YSize - 2)); Read the AutoDocs on this specific function! In the function Retrieve_FontPrefs() a number of macro's is used, which I would also like to share as they are mightily handy. Allocating a structure and clearing the entire structure with 0's:
  1. #define ALLOCATE_STRUCT(StrucType) (StrucType *)IExec->AllocVecTags(sizeof(StrucType), AVT_ClearWithValue, 0, TAG_END)
An additional IFF error specification (meaning NO error):
  1. #define IFFERR_NOERROR 0
Copy a string in an allocation of sufficient size:
  1. #define ALLOCOPY_STRING(Destin, Source) IExec->FreeVec(Destin); if ((Destin = IExec->AllocVecTags(strlen(Source) + 1, TAG_END)) != NULL) strcpy(Destin, Source)
A macro for use when in DEBUG (= NOT published) mode:
  1. #ifdef DEBUG
  2. # define ELSE_ERROR(x) else IDOS->Printf("ERROR: Failed to %s\n", x)
  3. #else
  4. # define ELSE_ERROR(x)
  5. #endif
NOTE WELL Be sure to free (i.e. IExec->FreeVec()) both Hint, struct TextAttr and font-an-size string when the program is done. Final note The use of the above depicted way is not only limited to GetFile gadgets, but everywhere where a lengthy pathname CAN occur!

Tags: 

Blog post type: