═══ 1. XFolder Programmer's Guide and Reference ═══  Notices  Introduction -- start here  National Language Support  The XFolder source code ═══ 2. Notices and Legalese ═══  Licence (CHANGED!)  GNU Public Licence Please see the XFolder Online Reference for more notices. ═══ 2.1. Licence And Disclaimer ═══ March 1999 While previous XFolder versions were "plain" freeware, all XFolder versions since V0.80 have been placed under the GNU General Public Licence (GPL) Version 2. You can find the full text of the GPL in the plain-text file COPYING in the XFolder installation directory. I have added a formatted version of this on the next page of this document. By installing XFolder on your system, you declare your agreement to the terms and conditions of the GNU General Public Licence, Version 2, as contained in the file COPYING in the XFolder installation directory. The GPL means more Legalese, but also more rights for you as a user and, if applicable, developer. The most obvious change is that XFolder's source code is now also released. The following brief explanations are intended as an introduction to the terms and conditions of the GPL only. They are not part of the licence agreement, for which only the terms and conditions in the COPYING file shall prevail.  If you are an XFolder user, not much has changed. You may still use XFolder without any payment to Ulrich MФller. The GPL does not restrict you on this matter, because it only imposes restrictions on distribution and modification of software. However, there is still no warranty whatsoever. If XFolder ruins your system and your work of the last 30 years is lost, sorry. (This is not very probable though). Refer to the bottom of the GPL for the detailed disclaimer.  If you're a distributor, you may still distribute the whole XFolder package. However, the GPL will probably require you to also distribute the complete XFolder source code, which is available in a separate package from the author's WWW homepage. Refer to the GPL for details.  If you're a developer, you now have the source code, which you may use and modify. However, if you base your own software on XFolder's source code, the GPL will most probably require you to place your work under the GPL also. This means that you cannot use my sources if you don't publish all your sources also. Even though I'm publishing the source code, I retain the copyright to what I've written. The GPL deals with these issues extensively. Before you use or modify any code parts of XFolder, make sure you read the GPL thoroughly. If your software is GPL'd also, by all means feel free to use my code. If you have any questions, feel free to contact me. This licence does not necessarily cover future releases. Ulrich MФller retains the right to modify the licence agreement for future versions. And now: have fun with XFolder! (C) Copyright 1997-99 Ulrich MФller. ═══ 2.2. GNU Public Licence ═══ For a formatted version of the GPL, check the XFolder Online Reference ("Notices" section). ═══ 3. Introduction -- Start Here ═══ Welcome to the XFolder source code! Changes made since the initial source code release (which was for V0.80) are logged in the changelog.txt file in the root directory of the source package. I hope this is fairly complete. You must unzip this package on a HPFS drive because many files in this package contain long file names. This file covers two different things: 1. Information about the XFolder C source code. This is mainly of interest for programmers who would like to contribute to XFolder, find bugs, or who are just interested in learning how the thing is functioning. Some more legalese: BEFORE USING OR MODIFYING ANY CODE, MAKE SURE YOU READ THE GNU GENERAL Public LICENCE IN ITS ENTIRETY. If I find out that you steal from me without complying to the GPL, you'll get into trouble. I am not releasing XFolder's source code to make it easier for shareware or commercial developers to sell better software, but to get freeware programmers to write WPS classes. However, if you follow the GPL, you are strongly encouraged to use any of my code. After all, I am also using public code. OS/2 needs more freeware! If you have questions regarding licence issues, please contact me. 2. Information about XFolder National Language Support (NLS). This is of interest for people who would like to translate XFolder only. No knowledge of C programming is required for this. See the "NLS Overview" page for more information. If you have questions, feel free to contact me at ulrich.moeller@rz.hu-berlin.de. But if you want to start writing WPS classes, please don't start asking me things like "What is an IDL file?". If you have never created a WPS class, please read the excellent courses at EDM/2 (www.edm2.com) first, which explain a lot of things to get you started. As far as I know, XFolder is the most complex WPS source code available, and it probably does not serve very well as a WPS beginner's tutorial. Thanks! ═══ 4. The XFolder Source Code ═══  Overview  Requirements for compiling  The /MAIN directory  The /HELPERS directory  XFolder function prefixes  Some SOM tricks  XFolder settings  Folder window subclassing  Menu manipulation  Extended sort functions  XFolder threads  Playing Sounds  XShutdown  XFolder Exception Handling  Debugging XFolder  Making XFolder ═══ 4.1. Overview ═══ Before going into the details of source code, which is complex enough to get even the more experienced programmer confused, please read the chapters in the "XFolder internals" chapter of the XFolder Online reference for a first introduction to XFolder's basic inner workings. This will give you a vague idea of what XFolder is doing where and deals with the most important concepts only, so you don't get overwhelmed by all the details which come in this file. The different subdirectories contain the different parts of XFolder.  The /001 directory tree contains all the National Language Support (NLS) for English.  The MAIN directory contains the XFolder main DLL (XFLDR.DLL) files, which is composed from all the .C sources. These in turn contain all the SOM/WPS classes and some library functions. This DLL loads the NLS DLL (XFLDRxxx.DLL, with xxx being a language code) at WPS bootup or when the language is changed using the "XFolder Internals" settings page. See this page for more.  The /HELPERS subdirectory contains a lot of C files with helper functions which are independent of XFolder. That is, these can be used with any OS/2 VIO and/or PM program. The functions are grouped into categories. See this page for more. The HELPERS dir also contains files from Dennis Bareis' PMPRINTF package, which is available in full from his homepage. Refer to the "Debugging XFolder" section for details.  The /NETSCDDE subdirectory contains the source code of the Netscape DDE interface and the INF sources. This is pretty straightforward, aside from the messy DDE stuff, which is only half documented, both by IBM and Netscape.  The /TREESIZE subdirectory contains the source code of the external TREESIZE utility, which is called if you select "Treesize" from a folder's context menu (new with V0.81). This might be quite useful to you if you're interested in learning some PM container programming, which I've only begun to understand while developing this utility. This also shows some techniques of how to multithread PM programs without blocking the system, which is not trivial.  /WPSRESET, /REPCLASS and /XSHUTDWN contain the sources of the little XFolder utilities of the same name. These are described in the "Related Files" section of the XFolder Online Reference.  /TOOLS contains some tools used by the makefiles. Put these on your PATH before making XFolder. ═══ 4.2. Required Tools ═══  Compiler. With V0.80, I have switched to IBM VisualAge C++ 3.0 to develop XFolder. I have not tried EMX/GCC because I'm too lazy to get used to it, even though it might compile faster. I'm not perfectly sure whether I'm using IBM-specific language extensions. I had to switch the language level to "extended" to use some none-POSIX C stuff, and I'm not sure whether this is working with GCC. I have looked up some functions in the EMX docs, such as _beginthread, and these appear to be the same, even though they're not POSIX C. If you get these sources compiled with a different compiler, please let me know or supply patches.  Whatever compiler you use, you need some OS/2 Developer's Toolkit for all the SOM header files. The Warp 3 toolkit is sufficient. XFolder does some tricks for Warp 4 too, but does it even though I don't have the Warp 4 toolkit. ;-) The Toolkit is also needed for changing NLS files, even though you won't need a compiler for that.  All the .C files have lots of debugging code which can conditionally be compiled using certain #define's in common.h. Only if you enable the debugging code, you need the full PMPRINTF package by Dennis Bareis. You do _not_ need that package if you don't use the debugging #define's (which is the default), so don't worry. Check the "Debugging" section of this README for details. Also see the "Making XFolder" section for how to rebuild XFolder. ═══ 4.3. The /MAIN directory ═══ The code for the XFolder main DLL (XFLDR.DLL) is in /MAIN. It uses a lot of code from the /HELPERS directory, which is new with V0.81 and contains all the helpers code which could also be used independently of XFolder. See the next page for more. The XFolder package contains many classes, but puts them all into a single DLL. This is possible by specifying the same target DLL in the IDL files. One can put several classes into one .IDL file (which happens in xfldr.idl for example) or spread them across several IDL files too. Note that the .DEF files are not used, except for one. These files are created automatically by the SOM compiler, which cannot however create a single DEF file if you use several IDL files for the same DLL. So I created xfjoint.def manually, which is used instead for linking. This file exports lots of mysterious SOM structures which allow the SOM kernel see the classes in the DLL. The filenames with xf* contain the XFolder WPS classes. The filenames are similar to the original WPS header files for the classes which are replaced by the XFolder classes. Here is a short overview. Some of the XFolder concepts are then laid out in more detail below:  xfdataf.* is for XFldDataFile.  xfdesk.* is for XFldDesktop only. With V0.84, lots of code has been moved to separate files from this file.  xfdisk.* is for XFldDisk. This just maps a lot of context menu features to the XFolder routines.  xfldr.* is for the XFolder (WPFolder replacement) class, probably the main part of XFolder. This also has the subclassed folder frame window procedure with all the bells and whistles. See "Folder window subclassing" for details. This also contains XfldStartup and and XFldShutdown folders, which are plain subclasses of XFolder.  xfobj.* is for XFldObject, which is needed to get access to some WPS internals and initialize the XFolder environment.  xfpgmf.* (new with V0.84) is for XFldProgramFile, which does the icon replacements for executable files.  xfsys.* used to be XFldSystem, now (V0.80) contains the two classes derived from WPSystem, i.e. XFldWPS and XFldSystem (for the "Workplace Shell" and "OS/2 Kernel" objects, respectively). This contains most of the code for the notebook pages. Common header files:  dlgids.h contains all the dialog id's which are used by both the NLS resource DLLs and the XFolder main DLL. DO NOT MESS WITH THIS FILE OR EVERYTHING WILL BLOW UP. This file has grown since the end of 1997 and is not very logical any more.  common.h contains both the funcs from common.c as well as lots of #define's which are _not_ used by the NLS DLLs to avoid having to recompile them all the time. I have done a lot of cleanup with the common.* files with V0.84. Other files:  apm.* contains the APM power-off code, thanks to ARAKAWA Atsushi, who filled this in (V0.82) (apm* function prefix).  classlst.* "WPS Classes" dialog and SOM logic (used to be objdlgs.* in V0.80) (cls* function prefix). See "Some SOM Tricks" for details.  cnrsort.* contains sort functions for container sorting only (used to be xfsort.* in V0.80). See "Extended sort functions" for details.  common.* contains lots of miscellaneous functions which are used by all parts of XFolder (cmn* function prefix), mostly to deal with XFolder Global Settings and other stuff which is independent of the SOM classes.  except.* (new with V0.84) contains the XFolder exception handlers which used to be in xfdesk.c (exc* function prefix). See "XFolder exception handling" for details.  menus.* (new with V0.81) contains all the menu logic which was previously in the xfldr.c file (mnu* function prefix). See "Menu manipulation" for details.  module.* contains XFolder's _DLL_InitModule function and some more functions to query information about the XFolder DLL (mod* function prefix).  notebook.* (new with V0.82) contains very useful notebook helper functions, which save me from having to rewrite the same window procedures for every single notebook page. Almost all XFolder notebook pages now use these routines (ntb* function prefix).  statbars.* (new with V0.81) status bar logic, previously spread across many files (stb* function prefix).  xshutdwn.* (new with V0.84) now contains all the XShutdown code which used to be in xfdesk.c (xsd* function prefix). See "XShutdown" for details.  xthreads.c.* (new with V0.84) now contains the Worker and Quick threads, which also used to be in xfdesk.c (xthr* function prefix). See "XFolder threads" for details.  xwps.c.* (new with V0.84) now contains all kinds of pseudo SOM functions which used to carry the cmn* prefix, but were spread all over the place and thus hard to find. All these functions have been put into this file and given the xwps* prefix. See "Some SOM Tricks" for details. And then there's SOUND.DLL, which is compiled from the sound.* files (snd* function prefix; see the "Sounds" section). ═══ 4.4. The /HELPERS directory ═══  animate.* contains helper functions to animate icons and also other animation funcs.  dosh.* Control Program helper functions.  eas.* contains helper functions to handle extended attributes.  gpih.* GPI (graphics) helper functions.  linklist.* contains helper functions to handle linked lists.  procstat.* modified Kai Uwe Rommel's DosQProc() functions.  progbars.* progress bar code.  prfh.* contains profile (INI) helper functions.  stringh.* contains helper functions to handle strings/texts  shapewin.* contains the ShapeWindows library. See the top of shapewin.c for details (new with V0.85).  threads.* contains helper functions to synchronize threads.  winh.* Win (PM) helper functions.  wphandle.* Henk Kelder's WPS handles functions (thanks again, Henk!)  prfh.* is new with V0.82 and contains Profile (INI) helper functions. ═══ 4.5. XFolder Function Prefixes ═══ With V0.81, I have rearranged a lot of files across many more files to make a bit clearer where the different functions reside. I guess nobody was able to overlook what was going on where. In addition, I have renamed many functions, giving them prefixes which show in which file they reside:  anm* /helpers/animate.*  apm* /main/apm.*  cmn* are now (V0.84) exclusively in /main/common.c.  dosh* /helpers/dosh.*  ea* /helpers/eas.*  exc* /main/except.* (new with V0.84)  gpih* /helpers/gpih.*  lst* /helpers/linklist.*  mnu* /main/menus.*  mod* /main/module.*  pbar* /helpers/progbar.*  prfh* /helpers/prfh.* (new with V0.82)  prc* /helpers/procstat.*  shp* /helpers/shapewin.c (new with V0.85)  stb* /main/statbars.*  strh* /helpers/stringh.*  thr* /helpers/threads.*  wph* /helpers/wphandle.*  xsd* /main/xshutdwn.* (new with V0.84)  xthr* /main/xthreads.* (new with V0.84)  xwps* /main/xwps.* (new with V0.84)  xf* SOM instance methods in the /main/xf* files  xfcls* SOM class methods in the /main/xf* files ═══ 4.6. Some SOM Tricks ═══ In general, XFolder is pretty straightforward SOM/WPS code. It overrides a lot of wp* and wpcls* methods. In order to avoid too much confusion, the methods which XFolder adds do not carry the usual wp* and wpcls* prefixes, but xf* and xfcls* instead. Only XFolder SOM methods have this prefix; "normal" functions have other prefixes. As XFolder became more complex over time, I have delved quite deeply into SOM. While I still don't fully understand what is going on in the SOM kernel (from what I hear, I guess nobody does) and some bugs in there keep puzzling me, but I've found out some interesting stuff. Most of this is related to the "WPS Classes" page in the new "Workplace Shell" object (xfsys.*). The SOM logic for this is in classlst.c in clsWPSClasses2Cnr, which can analyze the current SOM runtime environment (which is that of the WPS), i.e. all the classes that have been loaded and query their inheritance hierarchy at runtime. This inserts the WPS class tree as recordcores into a given cnr control. This works with any containers in tree views, so that some XFolder dialogs can use this too. Check the sources, there's some interesting stuff. Note that there are quite a number of functions in XFolder which take SOM (WPS) objects as a parameter, even though they are not SOM methods. See xwps.c for examples. The reason for this is that resolving SOM methods takes quite a bit of time, and calling regular functions will work just as well, but faster. If you look into the .IH header files created by the SOM compiler, you see that the C bindings for method calls are really all macros #define'd in the header files which translate into more complex C code. Here's an example: if you call _wpQueryStyle(somSelf) in xfldr.c, where this method has not been overridden, the WPObject version of this method should get called. Here's the #define in xfldr.ih for this: #define XFolder_wpQueryStyle WPObject_wpQueryStyle And WPObject_wpQueryStyle is #define'd in wpobject.h from the toolkit headers as follows: #define WPObject_wpQueryStyle(somSelf) \ (SOM_Resolve(somSelf, WPObject, wpQueryStyle) \ (somSelf)) Actually, there are more macros related to this, but this is the important one. SOM_Resolve in turn is a macro #define'd in somcdev.h, which calls the SOM kernel function somResolve. That function finally goes through the class method tables to find the actual function address and call it. As as result, not only does compiling of SOM code take ages (because of all the nested macros), but also calling SOM methods does, because as opposed to "static" OO languages such as C++, method resolution is occuring at run-time. IBM says in the SOM docs that calling a SOM method takes at least three times as long as calling a normal C function. Since there is no real need to write functions as SOM methods, except when you want to design a method which can be overriden in subclasses, I have only done so in rare occasions to speed up processing. Here are some other SOM tricks and functions which are not mentioned directly in the WPS reference (but only in the chaotic SOM docs), but these are very useful and should be known to everyone:  Since all the SOM stuff is declared in those huge header files, you need to #include a header file if you need access to certain class-specific features. For example, if you write a subclass of WPDataFile and need some program-object method call (e.g. _wpQueryProgDetails), you need to put #include on top of your code, or the method binding will not be found. This is not neccessary if the method is defined for a superclass of your class, because the SOM headers automatically #include all the parent classes. That is, for example, you don't need to #include wpfsys.h (for WPFileSystem) for your WPDataFile subclass.  The most important thing to keep in mind is that all SOM classes are objects too. They are instances of their respective metaclasses. This takes some getting used to, but it is this concept only which allows WPS classes to be created at runtime and for class replacements in the first place. That is, for example, any WPS object is an instance of WPObject (really one of its subclasses). The WPObject class in turn is an instance of its metaclass, M_WPObject. The WPObject class has a so-called "class object" at runtime, the metaclass does not.  For any existing class you always have a corresponding macro which is an underscore plus the class name. That is, the class object of WPObject can always be obtained with _WPObject.  BOOL _somIsA(pObject, pClassObject) checks for whether pObject is an instance of the class specified by pClassObject or one of its subclasses. This is extensively used in statbars.c to adjust status bar display. Example: _somIsA(somSelf, _WPObject) should always be true within the WPS context, since all WPS classes are subclasses of WPObject.  PSZ _somGetName(pClassObject) returns the actual name of the class specified by pClassObject, as specified in the IDL file. (That is different from wpclsQueryTitle.) Example: _somGetName(_XFldObject) will return "XFldObject".  SOMClass* _somGetParent(pClassObject) returns a parent class. Examples: _somGetParent(_WPProgram) returns the WPAbstract class object (_WPAbstract). _somGetParent(_XFolder) should return _WPFolder, unless there's some other WPFolder replacement class above XFolder in the replacement list.  BOOL _somDescendedFrom(pClass, pClassParent) returns true if pClass is descended from pClassParent. Example: _somDescendedFrom(_WPFolder, _WPObject) should return TRUE.  somResolveByName(pClassObject, "method") gives you a function pointer as implemented by the specified class. You should assign this to a function pointer variable which matches the function prototype, or otherwise you'll get crashes.  Class replacements. Nowhere is really documented how class replacements actually work, except that replacement classes must be direct descendants of the class which is to be replaced. I assume that class replacements are a feature of the SOM class manager, of which the WPS class manager is a descendant (WPClassManager is half documented in the WPS Reference.) At least there is a documented SOMClassMgr method, somSubstituteClass, which appears to be doing exactly this job. From what I've seen, class replacements seem to work this way: Any time a class object is queried, the class manager does not return the class object of the specified class, but the object of the replacement class instead. As a result, all the method calls are not resolved for the original class, but for the replacement class instead. Examples: wpModifyPopupMenu is not called for WPFolder, but for XFolder instead, even though the WPS had called it for a folder object. By contrast, for wpQueryIcon, which is not overridden by XFolder, method resolution leads to the method as implemented by the parent class, which should be WPFolder, unless some other class replacements is present. The class replacement mechanism is most obvious with the class object macros described above: if you have a _WPFolder in your code, this returns the class object of XFolder, i.e. _WPFolder == _XFolder. So if you absolutely need the WPFolder class object, you call _XFolder and then climb up the class object's inheritance tree using _somGetParent until _somGetName returns "WPFolder".  To get the class object of any class without having access to its header files, do the following: somId somidThis = somIdFromString("WPObject"); SOMClass *pClassObject = _somFindClass(SOMClassMgrObject, somidThis, 0, 0); Note again that _somFindClass will return replacement classes instead of the originals. (That's how the class object macros work, BTW.)  __get_somRegisteredClasses(SOMClassMgrObject) returns a SOMClassSequence of all currently registered class objects. This is used by the "WPS classes" page in the "Workplace Shell" object. See classlst.c for details. ═══ 4.7. XFolder Settings ═══ XFolder uses two kinds of settings: global and instance settings. The global settings are set both in the "Workplace Shell" object and on the "XDesktop" notebook page and are stored in the "XFolder" app in OS2.INI. All the global notebook logic is in xfsys.c. XFolder's code accesses these settings via a large structure called GLOBALSETTINGS (suprise!) defined in common.h, to which a pointer be obtained using cmnQueryGlobalSettings (common.c). By contrast, the individual object settings are stored in instance data, which is declared in the .IDL files. Most of these are stored and retrieved using the normal WPS mechanism (wpSaveDeferred/wpRestoreData) and can have a certain "transparent" value, which means that the global setting is to be used instead. ═══ 4.8. Subclassing Folder Windows ═══ XFolder subclasses all WPFolder frame windows to intercept a large number of messages. This is needed for the majority of XFolder's features, which are not directly SOM/WPS-related, but are rather straight PM programming. The trick is just how to get "past" the WPS, which does a lot of hidden stuff in its method code. By subclassing the folder frames, we get all the messages which are being sent or posted to the folder and can thus modify the WPS's behavior, since most of the default WPS methods are only called in response to certain folder messages. Subclassing is done in XFolder::wpOpen after having called the parent, which returns the new frame window handle. XFolder's subclassed frame window proc is called fnwpSubclassedFolderFrame (also in xfldr.c). Take a look at it, it's one of the most interesting parts of XFolder. It handles status bars (which are frame controls), tree view auto-rolling, special menu features, the "folder content" menus and more. This gives us the following hierarchy of window procedures: 1. our fnwpSubclassedFolderFrame, first, followed by 2. WPS folder frame window subclass, followed by 3. WC_FRAME's window procedure, followed by 4. WinDefWindowProc last. If additional WPS enhancers are installed (e.g. Object Desktop), they will appear at the top of the chain also. I guess it depends on the hierarchy of replacement classes in the WPS class list which one sits on top. In order to be able to relate the original frame window proc to a folder frame (which can be different for each WPFolder window), XFolder maintains a global linked list of PSUBCLASSEDLISTITEM structures (declared in common.h). This might be a bit slow, but I have found no other way to store the original window proc. Using window words does not work (believe me: it does crash the WPS), because the window words seem to be used by the WPS already, and (of course) this is not documented. If you have a better idea for this, let me know. As with all linked lists, XFolder uses the lst* functions in /helpers/linklist.c for adding/removing/searching etc. list items. I have developed these myself. If you have developed better funcs for doing this, let me know. I don't claim mine are too effective, but I have found no better ones which are thread-safe, and mine are because they allow for using mutex semaphores. ═══ 4.9. Menu Manipulation ═══ This is perhaps the most complex part of XFolder. After all, this is what I've started with, so some code parts may well be unchanged since December '97, when I neither knew much about C nor about WPS programming. With V0.81, all the logic for manipulating and evaluating context menus has been moved to a new file, menus.c. All these functions have been given the mnu* prefix to make this a bit clearer. The XFolder classes override wpModifyPopupMenu and wpMenuItem[Help]Selected for almost every replacement class. These overrides either handle the new context menu items in that method code or, for XFolder and XFldDisk, call the functions in menus.c, because these two classes share many common menu items, so we can share most of the code also. All newly inserted menu items use variable menu IDs. You can tell this from their #define names, which have an _OFS_ in their name (check dlgids.h). To all these values, XFolder adds the offset that is specified on the "XFolder internals" notebook page. For an introduction to how the config folder menu items work, see the "XFolder Internals" section in the XFolder Online Reference. XFolder uses a fairly obscure system of global variables to be able to relate menu items to their intended functions. All the variable menu items are stored in a global linked list when the context menu is opened for a folder. In each list item, XFolder also stores what kind of object (template, program object, folder content item etc.) the menu item represents. This list is then examined by wpMenuItemSelected to perform the corresponding action (create a new object, start program etc.). After that, the list is destroyed, so it only eats up memory while the context menu is open. All this code is now (V0.81) in menus.c. The folder content menus are initially only inserted as empty stubs. They are only filled with items when they're opened: XFolder intercepts WM_INITMENU in the subclassed folder frame proc (fnwpSubclassedFolderFrame, xfldr.c) and then populates the menu. These menu items are stored in the global list too and marked as folder content menu items so that wpMenuItemSelected will then simply open the corresponding WPS object. The folder content code has now been moved to menus.c also. Painting the icons is then done using owner-draw menu items; the messages which are necessary for this are also intercepted in fnwpSubclassedFolderFrame and then call corresponding functions in menus.c. Folder content menus are also subclassed to be able to intercept right-mouse button clicks. The new window proc for this is called (suprise!) fnwpFolderContentMenu and resides in xfldr.c. The xfSelectingMenuItem method introduced in V0.80 has been removed again with V0.81 due to the problems with SOM multiple inheritance. ═══ 4.10. Extended Sort Functions ═══ The default WPS sort functions are a mess, and I suppose IBM knows that, since they have hardly documented them at all. So here's what I found out. The default sort criteria are all related to object details. That is, to implement a sort criterion, you have to have a corresponding object detail, as defined in the wpclsQueryDetailsInfo and wpQueryDetailsData methods. There are many more object details than those shown in folder Details views. I have counted 23 here (but your results may vary), and since there are only 13 columns in Details view, there should be about 10 invisible details. (Side note: You can view all of them by turning to the "Include" page of any folder and pressing the "Add..." button.) Most of the details are defined in WPFileSystem's override of wpclsQueryDetailsInfo, and if certain sort flags are specified there (even when the details column is never visible), the WPS will make this detail available as a sort criterion. That is, it inserts the column title in the "Sort" submenu, shows it on the "Sort" page, and sets some corresponding comparison function for the folder containers automatically. This works beautifully for "real" subclasses of default WPS classes. For example, you can create some subclass of WPDataFile, e.g. "WPAddress" for storing addresses, define new object details for that class, set the "folder sort class" of a folder to WPAddress, and you can then sort the folder according to addresses automatically. So, in theory, the whole details concept is great. You add new details to a class, and the WPS can automatically sort, display the details, and even set the "Include" options for folder accordingly. But unfortunately, all the details/include/sort stuff is yet another example of another great IBM concept which was only half implemented. I have found out the hard way that XFolder cannot use this approch to extend the default sort criteria. This is for the following reasons (believe me, I've tried all of the following): 1. It is impossible to add new default details to an override of WPFileSystem. Even if WPFileSystem is replaced with another class, wpQueryFldrSortClass and wpQueryFldrDetailsClass do not return the replacement class object, but the original WPFilesystem class object. I have then tried to replace these two methods too, and details/sorting would still not always work. There seem to be some ugly kludges in the WPS internally. 2. Apparently, two sort criteria ("Name" and "Type") do not correspond to any details columns. Normally, sort criteria are specified by passing the corresponding details column to a sort function (see wpIsSortAttribAvailable, for example). However, these two criteria have indices of "-2" and "-1", so there seems to be some hard-coded special case check in the WPS again. The "Type" criterion is even available when WPObject is selected as the folder sort class, but WPObject does not define that detail. So who knows. 3. The documentation for CLASSFIELDINFO is incomplete and partly incorrect. I have tried to specify new sort functions in there, and the WPS would just hang. Apparently the function prototypes are not correct, because my own comparison function would never reach its first instruction before the hang. 4. The wpQueryFldrSort and wpSetFldrSort methods are completely obscure. They are prototyped using a PVOID parameter, and the documentation says this should point to a SORTFASTINFO structure. But for one, this structure is defined nowhere in the headers, and even more importantly, the data passed to these methods does not correspond to the description in the WPS reference. The first field in the structure is definitely no sort function. So we have one more mystery there. 5. The most important limitation was however that there are no default settings for sorting. The default sort criterion is always set to "Name", and "Always sort" is always off. This is hard-coded into WPFolder and cannot be changed. There is not even an instance method for querying or setting the "Always sort" flag, let alone a class method for the default values. The only documented thing is the ALWAYSSORT setup string. Besides, the whole concept is so complicated that I only understood it now after having debugged all the related methods for several days. I don't think many users appreciate the flexibility of that concept or even know what "sort classes" or "details classes" are about, even though there may be certain WPS classes that use this concept. The XFolder approach. It's basically a "brute force" method. XFolder lets the WPS add the default sort features to context menus as usual, then adds its own, and just intercepts all the menu ID's in wpMenuItemSelected. I have then written a complete new set of container sort comparison functions (in cnrsort.*) which do the job. So it's not the WPS at all any more which does the sorting, but XFolder, and XFolder uses its own set of settings for all this. See cmnSetCnrSort in xfldr.c for how this is done. As you know, XFolder replaces the "Sort" page in folder settings notebooks altogether too. In addition, XFolder overrides the wpSetFldrSort method, which apparently gets called every time the WPS tries to sort a folder. That is, normally, if one of the "Sort" menu items would get called, but these are intercepted already by XFolder. The override is still needed though because the WPS calls that method frequently when "Always sort" is on also, e.g. when you rename a file. Finally, there's a real neat hack to get access to the internal folder sort settings, i.e. the settings that reside in the memory allocated by the original WPFolder class. In wpRestoreData, which gets called when an object is awakened, WPFolder always attempts to restore its folder sort settings from the instance settings. Since the caller always passes a block of memory to which wpRestoreData should write if the data was found, we can intercept that pointer and store it in XFolder's instance data. We can therefore manipulate the "Always sort" flag also. BTW, this can be used as a general approach to get access to WPS-internal data, if a corresponding ulKey exists. Just override wpRestoreData (or the other restore methods) and check for what keys they get called. Most of them are declared in wpfolder.h in the Toolkit headers. ═══ 4.11. XFolder Threads ═══ XFolder is quite heavily multi-threaded and offloads most tasks which are probable to take some time to the so-called "Worker" thread. XFolder uses mutex semaphores all over the place to serialize access to global data structures. Note that with V0.84, all the additional thread code has been moved from xfdesk.c to a new xthreads.c file. This makes more sense and also reduces the danger of unwanted side effects with large code files. The Worker thread creates an object window to which other functions post messages using xthrPostWorkerMsg to offload work. This object window's window procedure is called xthr_fnwpWorkerObject, the main thread function is called xthr_fntWorkerThread. For example, the Worker thread maintains the global linked list of currently awake WPS objects. XFldObject::wpObjectReady post WOM_ADDAWAKEOBJECT to the Worker thread, which then adds the object to that list. (This list is needed by XShutdown to store all the awake WPS objects; see the XFolder Online Reference for details). The Worker thread is created in XFldObject::wpclsInitData, which calls xthrInitializeThreads. This is probably the first SOM method called when the WPS is booting up, so I put this in here to allow the Worker thread to keep track of really _all_ the objects. The Worker thread runs at "Idle" priority, unless more than 300 messages have piled up in its message queue. In this case, the priority is temporarily raised to "Regular" and set back if the message count goes below 10 again. This can happen when opening folders with a very large number of objects. In addition, XFolder uses the "Quick" thread running at a high "Regular" priority for things which won't take long but should not block the main WPS (Workplace) thread. This thread creates an object window also. For example, the new system sounds are played here. The Quick thread is created together with the Worker thread and also displays the most beautiful XFolder logo when it's started. ═══ 4.12. Playing Sounds ═══ The new system sounds are played in the "Quick" thread with high regular priority. Check fntQuickThread and fnwpQuickObject for details about how this is implemented using MMOS/2. There are two above-average programming tricks related to this:  For one, nowhere is documented how to access and modify the system sounds that appear in the "Sound" object. I have accidently discovered that this is actually fairly easy: these are stored in MMPM.INI in the MMOS/2 directory. Each sound has an index in that file, which I have declared in common.h (those MMSOUND_* #define's). These should be the same on every system. The INI data then contains a "##" string. Check cmnQuerySystemSound in common.c for details.  With V0.82, the actual playing of the sounds has been moved to a separate SOUND.DLL because previously XFolder would refuse to install on systems where MMPM/2 was not installed. This was due to the fact that if you link a DLL against the MMPM/2 library functions, loading of the DLL fails if these imports cannot be resolved. (Naturally, you don't get an error message other than FALSE from the SOM kernel. This has taken me days to find out.) Now, if loading of SOUND.DLL fails (either because it's not there or because the MMPM/2 imports of that DLL cannot be resolved), the new system sounds are silently disabled. This happens in xthrInitializeThreads. Also, I've added much more sophisticated MMPM/2 messaging to check for whether the waveform device is actually available. So playing sounds is a process of communication between the Quick thread and SOUND.DLL now. See the top of sound.c for more explanations. ═══ 4.13. XShutdown ═══ XShutdown (i.e. the "eXtended Shutdown" and "Restart WPS" features) has been moved from xfdesk.c to a new file, xshutdwn.c, with V0.84. XShutdown starts two additional threads to close all the windows. This is described in detail in the "XFolder Internals" section of the XFolder Online Reference. I have also tried to put plenty of comments into the sources. ═══ 4.14. XFolder Exception Handling ═══ XFolder registers additional exception handlers in certain parts where I considered worth it. With "worth it" I mean the following situations: 1. Certain code parts crashed on my system and these parts seemed error-prone enough to me to outweigh the performance loss of registering and deregistering exception handlers. 2. Exception handlers must be registered for all additional threads. If no exception handling was registered there, crashes would take the whole WPS down, because the default WPS exception handler only deals with the default WPS threads. 3. Exception handlers must also be registered every time mutex semaphores are used. If a code part crashes while a mutex semaphore has been requested by a function, all other threads waiting for that semaphore to be released will be blocked forever. And I mean forever, because even DosKillProcess won't be able to terminate that thread. You'd have to reboot to get out of this. So the exception handler must always check for whether a mutex semaphore is currently owned by the thread and, if so, release it. XFolder does not use the VAC++ library funcs for all this, but installs its own set of quite complex exception handlers (using DosSetExceptionHandler()). With V0.84, the exception handlers have been moved from xfdesk.c to a new file, except.c. That makes more sense. Also, I have created a few handy macros which automatically register and deregister the XFolder exception handlers. This avoids the frequent problem that one forgets to deregister an exception handler, which leads to really awkward problems which are almost impossible to debug. Those macros are called TRY_xxx and CATCH to mimic at least some C++ syntax. See except.c for how to install these macros. The XFolder exception handlers are the following:  excHandlerPlus is the one that makes the loud sounds and writes the XFLDTRAP.LOG file which is well-known to many XFolder users (grin). It uses a longjmp() to get back to the function, which might then react to that exception by trying to restore some safe state of the thread. See the func header for details about how this works. This handler is used by all the additional XFolder threads and also by the subclassed folder frame and folder content menu window procs. This slows down the system a bit because the handler must be registered and deregistered for each message that comes in, but there's no other way to do this. (I think.) With V0.84, I have added lots of debugging code which I found in the EXCEPTQ.ZIP package at Hobbes. The exception handlers are now capable of finding symbols either from debug code (if present) or from a SYM file. See the top of except.c for details.  excHandlerQuiet is similar to excHandlerPlus in that it uses a longjmp() also, but neither is this reported to the user nor is the logfile written to (that's why it's "quiet"). This is used in places where exceptions have ben known to occur and there's no way to get around them. I created this handler for V0.80 because I found out that somIsObj() is not a fail-safe routine to find out if a SOM pointer is valid, even though IBM claims it is. (These were the strange errors in the Worker thread which appeared in V0.71 when folders with subfolders were deleted, because the Worker thread then tried to access these objects when the folder was populated right before deletion.) So I created the xwpsCheckObject func which can return FALSE, using this handler, if access to the object fails. ═══ 4.15. Debugging XFolder ═══ Debugging WPS applications can be really tiresome, because you have to restart the WPS for every tiny change you made to the source codes to take effect. And, as with any PM program, you can't just printf() stuff to the screen. Even worse, it's hard to use the PM debugger, because you have to start the whole WPS (PMSHELL.EXE) with it, since XFLDR.DLL is no standalone application. So I had to look for something else. Those _Pmpf(("xxx")) thingies are for the magnificent PMPRINTF package by Dennis Bareis. These only display anything if the proper DEBUG_xxx #define's are set on top of common.h. For the release version of XFolder, all these flags have been disabled, so no additional code is produced at all. You thus don't have to remove the commands to speed up XFolder, because this wouldn't make any difference. Some files from the PMPRINTF package are included so that you can compile. The PM interface which actually displays the messages is not however. Check Dennis Bareis' homepage at http://www.ozemail.com.au/~dbareis/ where you'll find tons of other useful utilities too. However, to really use PMPRINTF, you'll have to put some DLLs on your LIBPATH. See the PMPRINTF docs for details. I strongly recommend using this utility. IMPORTANT NOTE: If you #define any of the flags at the top of common.h, you must have the PMPRINTF DLLs somewhere on your LIBPATH, or otherwise you'll spend days figuring out why XFolder is simply not working any more. (I had this recently after a reinstall of OS/2, after which the DLLs where gone.) That is, XFolder classes will not load at WPS startup, because the PMPRINTF imports cannot be resolved. Neither will registering XFolder classes succeed. And don't expect to get an error message other than FALSE. :-( _Pmpf(("xxx")) uses regular printf syntax, except for those strange double brackets, which are needed because macros don't accept variable parameter lists otherwise. Take a look at the top of common.h to find out more about the debugging flags. ═══ 4.16. Making XFolder ═══ If you wish to build the whole XFolder thing including the NLS DLLs and the INF and HLP files, do the following: 1. Put the .EXE files in /TOOLS directory in a directory on your PATH. These tools are required by the makefiles. 2. In MAKE.CMD in the main directory, you'll have to set a few variables. This is explained in that file. 3. Call MAKE.CMD, which will recurse into the directories and possibly take a long time. This will build the main DLL, the NLS DLL, and all the INF and HLP files. Note: Depending on whether the XFRELEASE environment variable is set, XFolder will be compiled with debug code or not. This is determined in /main/xfldr.mak. If this variable is not defined, you'll get the regular XFLDR.DLL. If this variable is set to anything, a lot of debug code (including line information) will be compiled into XFLDR.DLL, making that file more than 1 MB in size. This variable can be set in MAKE.CMD. Makefiles. Each directory has its own makefile. All makefiles were written for IBM NMAKE. I don't know if these will work with other make utilities. /HELPERS no longer has its own makefile, because the sources in here will only be used by the other XFolder parts, i.e. XFLDR.DLL, TREESIZE.EXE, NETSCDDE.EXE. The .OBJ files for the helper sources are then created in the directories of the respective XFolder part, because we might need different compiler options for each part. Rebuilding the whole thing. If you want to make sure that all C files are recompiled, open and save /helpers/dosh.h to make it newer than the target files. This is #include'd in all XFolder C files, and the makefiles' inference rules will then enforce recompilation of all C files. Alternatively, you can use CLEANUP.CMD, which will delete all the produced files. ═══ 5. National Language Support (NLS) ═══  Overview  Requirements for translations  The main NLS DLL (XFLDRxxx.DLL)  The INF and HLP files  The HTML2IPF utility  Images and screenshots  Other files  Testing your NLS files ═══ 5.1. Overview ═══ Note: If you have already started translating XFolder, please do read the changelog.txt file. There are important changes, or XFolder 0.81 might not run with your NLS files properly. Legal notes. XFolder is placed under the GPL, including the National Language Support (NLS). If you translate the NLS part to your language, you create "a work based on the Program" in the GPL sense. As a result, the GPL automatically applies to your translation too. This means that you have to publish your source code also. Now, to move away from the Legalese, I suggest that you do not distribute XFolder NLS packages yourself. Note that this is not a legal requirement (you may do so if you ship the source code also), but for practical reasons, mainly the following two: For one, all the NLS packages should be available from my homepage for convience. Secondly, since I might update XFolder after you have finished your work on your NLS version, XFolder might have trouble cooperating with your files. Internal IDs might change and/or new features might be added. So if you have finished creating your NLS package, please contact me, and I will put in on my homepage and upload it to the usual places where XFolder is normally available. This seems like the best solution to be, because I have a list of places which carry XFolder, and I can update all the NLS packages myself when anything changes with XFolder. This way, my homepage will always be the place to look for up-to-date NLS packages. Thus, when you're done translating, please zip up the complete directory tree again and mail the ZIP file back to me. How it works. XFolder NLS packages identify themselves to the XFolder core via country codes, as described on the "COUNTRY" page in the OS/2 Command Reference (CMDREF.INF). This package is prepared for creating the English NLS package, as it comes with the default XFolder package. The language code for English is "001". So the first step you'll have to take is finding out your country code. For example, Italian would be 039. Unfortunately, XFolder's National Language Support (NLS) is spread across quite a number of files, which have different file formats. These files are:  The NLS DLL to be put into the XFolder BIN subdirectory. This file contains the language-dependent stuff which is needed by XFLDR.DLL. notably dialogs, menus and miscellaneous strings. The NLS DLL is called XFLDxxx.DLL, with "xxx" being your three-digit language code. The files neccessary to translate this DLL are in the 001 directory. See "The main NLS DLL" for details.  The XFolder message file introduced with V0.80 residing in the HELP subdirectory. This file holds all kinds of messages which are mostly displayed in XFolder's message boxes and are too large to be put into the resources of the NLS DLL. This file is called XFLDxxx.MSG, with "xxx" being your three-digit language code. The source file for the MSG file is called XFLDRxxx.TXT (in the 001 directory also), with "xxx" being your three-digit language code. See "The main NLS DLL" for details.  The XFolder Online Reference (INF) in the XFolder main directory. This file is called XFLDxxx.INF, with "xxx" being your three-digit language code. The files neccessary to translate this file are in the 001/INF.001 subdirectory. See "The INF and HLP files" for details.  The XFolder HLP file in the HELP subdirectory. This file holds all the help panels that are displayed when you press F1. This file is called XFLDxxx.HLP, with "xxx" being your three-digit language code. The files neccessary to translate this file are in the 001/HELP.001 subdirectory. See "The INF and HLP files" for details.  The WPS class descriptions which are displayed on the "WPS Classes" settings page in the "Workplace Shell" object. This was introduced with XFolder 0.80 also. This file is called XFCLSxxx.TXT, with "xxx" being your three-digit language code. It also resides in the 001 directory. See "Other files" for details.  A number of REXX .CMD and text files for XFolder's installation. The files to be translated are in the MISC subdirectory. See "Other files" for details.  The SmartGuide (Warp 4) script used for the XFolder introduction, which is displayed right after XFolder has been installed and the WPS has been restarted. This (XFLDRxxx.SGS) is also in the MISC directory. See "Other files" for details. The TOOLS directory contains a valuable tool for converting HTML files to the IBM (IPF) format. This will be described on a separate page. This directory also contains a few other needed tools. See the separate README in that directory. BTW: I'm really curious how "Snap to grid" sounds in your language. ;-) ═══ 5.2. Required Tools ═══ Even though all the source files in this package can be edited using any editor (because they're all plain text files), you will need some extra utilities to create the actual NLS files. In any case, a C compiler is not required. To be more precise:  The message file (XFLDxxx.MSG) can only be created if you have any version of an OS/2 Developer's Toolkit (even 2.x should work), because you will need MKMSGF.EXE to convert the .TXT file to .MSG format. See "Other files" for more on this.  The same applies to the .INF and .HLP files: to build these from the .IPF sources, you will need IPFC.EXE from the Toolkit; see "The INF and HLP files" for details.  The makefiles in this package were written for IBM NMAKE, which comes with all the IBM compilers. I'm not sure these will work with other MAKE utilities. I'd be grateful for feedback. The makefiles are only for conveniently updating the target NLS files. If you don't have NMAKE, you have to assemble the files manually. See the following pages for details. ═══ 5.3. The NLS DLL ═══ The files in the "001" directory contain everything neccessary to compile a resource DLL (XFLDRxxx.DLL, with xxx being your country code). The resource DLL contains strings, dialogs, and notebook pages. Although the resources are contained in a DLL, the DLL contains no "real" code (except for one dummy function, because every DLL needs to contain at least some code). Necessary preparations. Most files in this directory carry a three-digit language code in their respective names; the files are prepared for English (language code 001). 1. For your language, you need to change all the filenames with "001" in their names to your country code (e.g. 039 for Italian). 2. You will also have to change the .DEF and .MAK files, which assume a country code of 001 at this point. Required changes are noted in the files themselves. Note that all the dialog ID's access the dlgids.h file in the /MAIN directory. Do not change anything in that file, or your WPS will behave really funny with your NLS DLL. If you're lucky, that is. ;-) Files that need to be changed:  xfldr001.def:The module definition file. Required changes are noted in the file itself.  xlfdr001.mak: Makefile for IBM NMAKE. Required changes are noted in the file itself.  xfldr001.rc, xfldr001.dlg: These are the main resource files which need lots of changes. I've documented everything you need to change in the .RC file. The .DLG file is included when the RC files is recompiled. It contains all the XFolder dialogs (i.e. notebook pages and other dialog windows). Sorry, there are no comments in there because I'm using DLGEDIT.EXE to create the dialogs, which rewrites the DLG file at every change, so all comments in there get lost.  xfldr001.txt: This is the "source" file for the XFLDR001.MSG file. You will need MKMSGF.EXE from the OS/2 Warp Toolkit to convert this plain text file into a .MSG file. See the notes in that file. Using a dialog editor. You can use DLGEDIT.EXE, the IBM dialog editor from the Developer's Toolkit, to change the dialogs. (This will not affect the .RC file.) For this, you will need to create the .RES file first (see "Recompiling" below). If you then open the .RES file with the dialog editor, you can choose /main/dlgids.h as an include file so that all the numerical ID's have a more meaningful name. When you then save the file, the .RES and .DLG files will be recreated. The .RC file remains untouched though. The dialog editor has a helpful "Translate mode" in its "Options" menu which disables a lot of menu items so you don't accidentally change dlg ID's or other important stuff. I don't know if you can use the URE editor also, I have not tried that. Recompiling the DLL. If you have IBM VAC++, you can simply use MAKE.CMD to have the DLL recreated. Everything should work fine, since all the neccessary files are included. Otherwise, things get a bit more complicated. Since the resource compiler (RC.EXE) is already included with every OS/2 installation, you can try the following: 1. Copy an existing XFolder resource DLL into your NLS DLL source directory; rename it so that it contains your language code. 2. Open a command line in that directory. 3. Type rc xfldr001.rc xfldr001.dll (replace "001" with your language code), which should create a new .RES file and link it against the existing DLL. After recompiling, you can test the DLL as described in "Testing the DLL". ═══ 5.4. The INF and HLP Files ═══ The sources for the INF and HLP files are written using a slightly extended HTML syntax. Creating INF and HLP files is therefore a two-step process: 1. The HTML sources are translated into an .IPF source file, using the HTML2IPF utility. 2. Next, the .IPF source is fed into IBM's IPFC.EXE from the Developer's Toolkit. This file is required to produce INF and HLP files, sorry. Please use the MAKE.CMD file on the top level of the XFolder source files to have the doc files created. This might take a while. You will first need to set a few environment variables on top of MAKE.CMD, which is documented in that file itself. You then have two alternatives for the translations:  I recommend translating all the .HTML files and keep using MAKE.CMD to have the HTML files converted into a single .IPF file (using the HTML2IPF utility), which can then be fed into IPFC.EXE. This has the advantage that you'll only have to change some panels in future versions, of which I will keep track. Also, I always note changes to the INF and HLP files in the HTML source files in HTML comments tags, so you can easily search for what's changed. The disadvantage is that, being a REXX program, HTML2IPF is slow.  You can also translate the one .IPF file in each of the INF and HELP directories directly, if you're familiar with the IPF source language, which is a proprietary IBM format (and which I don't like at all). To do this, you'll have to create the two IPF source files first, using MAKE.CMD. You can then translate this file and feed it into the IPFC.EXE compiler to have the .INF and .HLP files created. This has the disadvantage that if certain panels change in future versions, you'll have no clue what parts changed in this single IPF file, because the comments about changes are only visible in the HTML sources. If you still want to use this method, please change the text only. Do not change any resources, IPF tags, and especially not the resource ID's, or XFolder will not find its help panels any more. The "root" file in each of the two source directories is called XFLDRxxx.HTML, respectively, with "xxx" being your country code (which you should change in this one filename because HTML2IPF will use this name for the target IPF file also). To use HTML2IPF manually (instead of through MAKE.CMD), open a command line in the INF or HLP directory (with XFolder ;-) ), and type: \html2ipf xfldr001.html This will create a XFLDRxxx.IPF file (in the above example, XFLDR001.IPF). You can then run ipfc XFLDRxxx.IPF to compile a HLP file; add an /INF switch to produce an "INF" file. You must have the INF or HLP directory as your current directory, or HTML2IPF will tumble. If you don't have the Developer's Toolkit, there's no way you can actually produce the INF and HLP files. You will have to use Netscape or WebExplorer to view the help files, which of course are on many separate pages then. Begin viewing XFLDRxxx.HTML in the INF and HELP directories, respectively. In any case, mind these important notes:  The (slightly awkward, I admit) structure of the files is the following, in both directories:  XFLDRxxx.HTML is the "root" file. Files beginning with "0" appear in the contents tree of the produced INF and HLP files. All other files are somwhere below in the INF/HLP table-of-contents hierarchy, depending on the SUBLINKS tags in the HTML source files.  Do not change filenames! HTML2IPF sorts the IPF pages internally alphabetically according to the filenames, that's why I've implemented these strange naming conventions. I admit that since the structure has developed over time, it may not seem very logical to you, but it works. Changing things here will result in a lot of work, since all the links in the HTML files will have to be renamed also. Also, if you change the linkage of the HTML files, the resource IDs of the help panels will be altered, and XFolder will then get confused displaying the proper help panels.  Within the HTML files, do not change anything within angle brackets (""). Translate only the text outside of these. Even though some tags might not be good HTML style and the HTML files might not look pretty when viewed with Netscape, certain tag combinations (especially the
  • combinations) are neccessary to make the pages look good for IPF.  Don't forget to translate the page titles (in between the tags). This is what appears in the title of a panel window and in the "Table of contents" tree. I've forgotten this many times...  There are a number of files in the HLP directory which seem pretty useless. These are neccessary however in order to guarantee a certain sequence of the help panels in the resulting HLP file, on which XFolder relies for calling a certain help panel. I don't quite understand myself anymore how this order works, but it does, so I don't care. ;-)  I do not recommend translating everything for the INF book. I have done the German translation myself, and I think that the "Revision history" section makes no sense translating, because it is frequently updated and NLS versions didn't exist for the older versions anyway. Also, the "XFolder Internals" pages apply to programmers, which need to know English anyway, because otherwise they won't find their way through the required Toolkit docs either. So I think you can save yourself some work there. ═══ 5.5. The HTML2IPF Utility ═══ The TOOLS directory contains the very valuable HTML2IPF tool by Andrew Pavel Zabolotny. This REXX script is capable of creating a single .IPF file from a list of linked HTML files, which can then be passed to IBM's IPFC.EXE for creating .HLP and .INF files. I have slightly modified this tool (see notes below). Even if you don't know what I'm talking about, in this directory, you need to open HTML2IPF.CMD and change three, maybe four things:  In line 27, translate the Resources on the Internet string.  In lines 28-31, translate the string beginning with This chapter contains... translate every line, but do not mess with the '||'0d0a'x codes and such.  In lines 34/35 translate the Click below to launch [code] with this URL: string, but do not change the code in the middle.  In line 20, change the image converter; this is thoroughly explained in the Images section. Additional information (not obligatory) I have included HTML2IPF.INF, which describes how the tool works. You need not bother with this though, because everything is already set up properly. I have changed HTML2IPF.CMD in a few places to allow for IPF window positioning and certain extra character formatting. These changes are only documented here, in case you're interested -- you will NOT find these changes in the INF file (you don't have to know this, just if you're curious):  The <HTML> tag has new attributes: XPOS= and YPOS= work just like IPF's "x" and "y" tags; WIDTH= and HEIGHT are the same as in IPF.  Added the strings described above to global variables for NLS.  The <A> tag accepts a new "AUTO=" attribute, which works just like "HREF=", but automatically opens and closes the window (this is, for example, used on the "Introduction" page of the XFolder Online Reference).  The <CITE> and </CITE> formatting tags are now set to use a non-proportional font, which is used extensively.  Some formatting changes (<UL>, <BR>, <B> etc.).  HTML2IPF now removes indenting spaces at the beginning of lines because these would all appear in the INF/HLP files, while HTML ignores them. (New with V0.81). ═══ 5.6. Images and Screenshots ═══ The images in both the INF and HELP directory are always available in both GIF and uncompressed OS/2 1.3 BMP format. The reason for this is that HTML does not support BMP, and IPF does not support GIF. :-( HTML2IPF is very comfortable in this respect: it will automatically convert all GIF images to BMP format every time it finds an <IMG> tag, but only if no BMP file of the same filestem was found (see HTML2IPF.INF for details). The image conversion program which HTML2IPF requires is specified in line 20 of HTML2IPF.CMD. The original had Image Alchemy in here, I have changed it to "GBMSIZE", which is part of the freeware "Generalized Bitmap Module" (GBM) package available at Hobbes.  If you don't have GBMSIZE on your PATH, you can add the path to this line.  If you wish to use a different converter, specify it here. Be careful though: IPFC only supports uncompressed OS/2 1.3 bitmaps (see HTML2IPF.INF). That is, neither compressed bitmaps nor Windows or OS/2 2.0 bitmaps work. This is annoying, because IPFC's own image compression is totally outdated, but there's nothing we can do about this. I'd be very grateful if you could create your own screenshots for the online documentation, because I own German versions of OS/2 and for your NLS package, screenshots of your language are preferrable, at least for those pictures which have language-dependent stuff in them. I have used the following settings for the screenshots (just for your information, you don't have to use these): Fonts used: Titlebars: Humanist 521, 13 points (available on the CorelDraw 4 CD in Type 1 format) All the other fonts are set to 9.WarpSans. CandyBarZ installed, colors: Active: top 191/0/0, bottom 52/0/0 Inactive: top 160/160/130, bottom 40/40/40 Oh yes, XFolder installed. ;-) Now, if you create your own screenshots, save them as GIFs with the exact filenames of the originals (e.g. trunc.gif); you must then DELETE the respective BMP file, because HTML2IPF will only call the image converter for BMP if no BMP file of the same name exists. Tricks to reduce file sizes: Keep in mind that HLP/INF files have their own compression scheme which works best when much redundant data is in the bitmap files. That is, large areas of a plain color can be compressed best. So taking screenshots of folders with background bitmaps is a no-no, because this will really blow up the files. And use as few colors as possible. You can use PMView to reduce the number of colors to, say, 12 colors, which usually still looks alright. Make sure you don't have "dithering" or "error diffusion" enabled, because this will add lots of extra pixels which will make compression less efficient. Also, if you need to scale images to a smaller size, make sure not to enable any "interpolation", i.e. introducing extra pixels to make colors smoother. This adds lots more colors to the file, which cannot be compressed well. ═══ 5.7. Directory "MISC" ═══ The MISC directory contains files used by XFolder's installation script (INSTALL.CMD) plus the SmartGuide Script used for the XFolder introduction.  INSTxxx.MSG (with "xxx" being your country code, which you need to change) is -- I'm sorry -- in a proprietary format. It's not difficult to understand though: The file is used by XHELP.CMD (in the XFolder package), which is capable of extracting single text messages in between the <TOPIC>; and </TOPIC> tags in this file. The text between these tags is then displayed on the screen. Please note that this file will disappear in a future release of XFolder, as soon as the WarpIn installer is functioning, which I am currently working on. See my WWW homepage for details. If you still wish to translate this file, what you need to do here is simply translate all the text which follows a <TOPIC> tag. The text is displayed "as is", and no formatting is performed; as a result, you must take care that no more than 80 characters are contained in a line. You also should take care of the line breaks: it makes a difference in output whether a </TOPIC> end tag is found at the end of a line or at the beginning of a new line, because in the latter case, the line break is still printed to the screen. Just one more note: Do not change the keys mentioned in this file ("X", "Y", "N"), even if your language does not use "Y" for saying "Yes". Unfortunately, INSTALL.CMD relies on these keys. :-(  CROBJxxx.CMD is a straightforward REXX script which creates the default XFolder Configuration Folder. Even if you don't know REXX, don't worry: you only have to change the strings on top of the file, which contain all the language-dependent things. Be careful with the quotes. Do not change anything else, because XFolder relies on it.  SOUNDxxx.CMD is the REXX script which creates the neccessary INI entries for having the new XFolder system sounds in your "Sound" object. Only change the strings at the top of the file to your language.  XFLDRxxx.SGS is a Warp 4 SmartGuide script to display the "Welcome" window after XFolder has been installed and the WPS has been restarted. I have no idea what the precise syntax for these files is (once again has IBM developed good software, but then lets it rot), but it seems to be some HTML-like syntax, except that the tags MUST be in lower case, or they won't work. ═══ 5.8. Testing Your NLS Files ═══ XFolder's NLS depends entirely on a single entry in OS2.INI ("XFolder"::"LanguageCode"). This entry defaults to a "001" string and can be changed either at installation time (if the install script does it) or by using the "Language" setting in the "Workplace Shell" object, "XFolder" page 2. All NLS resources are loaded depending on this one setting. That is, if you change the language, XFolder expects not only your NLS DLL to be present in /BIN, but also the MSG, HLP, and WPS class description files in /HELP. So if you have not translated these yet, you might want to create a copy of the English ones with your language code in the filename. To test a new version of your NLS files:  Make sure you do not currently have your new NLS DLL selected on the "XFolder Internals" settings page, because if you do, the DLL is locked by XFolder and cannot be replaced. Select "US English" instead, which will unlock the previously used DLL, which can then be deleted from the XFolder directory. Then put your new DLL into the BIN subdirectory and open the settings again; select your DLL and see if things work.  Additional caveat for INF/HLP files: Before compiling the .IPF to the .HLP/.INF files, you should make sure that the target file is not currently in use. With INF files, that's easy: simply close it if it's open. With HLP files, you have to keep in mind that the WPS keeps these files locked even after you've closed a help panel window. However, the WPS only ever keeps a single HLP file locked at a time, so in order to unlock the XFolder HLP file, simply open a default WPS help panel, e.g. by selecting "Extended Help" for the Desktop window. You can then copy the new XFolder .HLP file to the XFolder HELP directory. (MAKE.CMD will do this automatically.) ═══ 6. Resources on the Internet ═══ This chapter contains all external links referenced in this book. Each link contained herein is an Unified Resource Locator (URL) to a certain location on the Internet. Simply double-click on one of them to launch Netscape with the respective URL. ═══ 6.1. http://www.edm2.com ═══ Click below to launch Netscape with this URL: http://www.edm2.com ═══ 6.2. http://www.ozemail.com.au/~dbareis/ ═══ Click below to launch Netscape with this URL: http://www.ozemail.com.au/~dbareis/ ═══ 6.3. http://www2.rz.hu-berlin.de/~h0444vnd/os2.htm ═══ Click below to launch Netscape with this URL: http://www2.rz.hu-berlin.de/~h0444vnd/os2.htm ═══ 6.4. mailto:ulrich.moeller@rz.hu-berlin.de ═══ Click below to launch Netscape with this URL: mailto:ulrich.moeller@rz.hu-berlin.de ═══ 6.5. notices_3comments.html ═══ Click below to launch Netscape with this URL: notices_3comments.html