CompuServe Thread

#AM TCL Lists

10 messages in this thread
#7194From: Burt JohnsonJul 20, 1993 3:23 AM
I am slowly getting things working here, but keep running into questions of how to properly proceed. It doesn't seem to me that the AM manual helps much in explaining what the classes are that AM generates and how to use them (that info it fine for C though). At any rate, I have a dialog with 2 lists — a log list and an error list. The data to be inserted in them will be generated at run time by the program as it performs tests on NuBus cards. The basic code was generated by AM 1.5.4X. I don't see how I should add new rows to each of the lists given the code basis that was generated. It looks as though the generated code expects me to maintain a ghost data structure with all the info rather than rely on the Mac List Manager to hold the data. Is that true? If so, is there a reason for this doubling of data requirements? I also don't see any method for adding to the list after it is created. I see where it can be built if the contents are known at creation time, but I expected to find a "AddListRow()" method. Is there something here I am misunderstanding? Given the AM structure, how should a method under another class (I created a "CCard" class) access the list? I tried to create a "CLogListRinger::Report (char *str)" function, but then couldn't figure out where to get my handle to the list element. ie, what is "x" in "x->Report("This is the next line")"? – Burt
#7213From: Rich ParkerJul 20, 1993 11:52 AM
Burt, If you're using the TCL and AppMaker-generated lists, then they are subclasses of the TCL's CTable class, which doesn't use the Mac's list manager at all. In this respect, lists aren't limited in size, as they are with list manager lists. You _do_ have to maintain an external structure that contains the data for your list entries. I used the CArrayPane (CAMArrayPane) class in my book to display the contents of my spreadsheet data. The advantage of the CArrayPane class is that in addition to the 2-D table, it has an _associated_ array that's fairly tightly coupled to the table. The advantage to the CArrayPane class is that any changes to the array cause BroadcastChange messages to be issued, which your CArrayPane instance will receive directly in its ProviderChanged method. You can add rows or columns to the lists by using the methods in CTable for this purpose (AddRow, AddCol, etc.). Check out my code which does this in Chapter 8 (page 204) of the Easy Object Programming book (THINK C edition). The best way for a method in another class to access the list is as follows: 1. First, it's usually best if the data associated with your list is kept in an array which is owned by the CMyData subclass. AppMaker generates code to make the handle to the CMyData instance available to most other classes. 2. Create an "access method" in the CMyData class that provides access to the "array", and not the list itself. By changing the contents of the array (if you're using the CArrayPane/array combination, or via an explicitly defined dependency relationship), the list will receive messages indicating the nature of the changes (add, delete, modify) via its ProviderChanged method. In the case of the spreadsheet in my book, I really should have placed the array under the complete control of the CEnsembleData class. Although it owns the array, I didn't provide access methods for it, but rather transmitted its handle to the classes that needed to access its contents. In retrospect, I think I'd create access methods in the CEnsembleData class and require the other classes to call these to access the data. Nevertheless, to access the list, you access the data structure in which the data _for_ the list is stored. The TCL's lists have no memory. The list gets displayed by your CArrayPane's (or CTable's) GetCellText method, which is called automatically by the TCL for each item in the list, when it determines that the list needs to be redisplayed (either initially, or later). Changes to the array in a CArrayPane instance can force an update of the list. The GetCellText method provides the means to pass back a simple string for a given list entry. If you require more elaborate formatting (as was the case in my styled text version of the spreadsheet (shown in Chapters 9-11 of the book), then you can also override the DrawCell method (which then must call your GetCellText method to retrieve the data to use for drawing). The DrawCell method can draw _anything_ you like, including styled text, graphics,…whatever. I hope this helps, -rich-
#7239From: Burt JohnsonJul 20, 1993 7:44 PM
Thanks for the detailed response! The reference to a specific page in your book is a big help too, as I have trouble finding specific info in there (I earlier suggested an improved index for just that reason). I'll give it a try and you'll see me again if I get stuck on another point!:-) – Burt PS: It seems that this "who owns what and how do I tell it what I want" is the hardest part of transitioning from C to C++.
#7295From: Rich ParkerJul 21, 1993 1:19 PM
Burt, You're welcome…and I appreciate your suggestion regarding the index. If I get a chance to revise the book I'll try to do a more comprehensive job. Actually, it's not the transition from C to C++ that seems so difficult, I think it's the transition from procedural to object-oriented design that causes the most problems. One currently popular model for application structure is the "client/server" model. If you look at the CMyData class in your application as the "server" for all of the other modules (because it is a direct descendent of the CDataFile and CFile classes and does all of the I/O), then it might make sense to you for that instance to own all of your data and provide it, upon request, to other modules in the application (which, of course, would be its clients). Getting past that one hurdle seems to help in making the breakthrough to OO design. In my book, you'll see that because the text window contains an instance of CEditText (which actually uses the Mac's TextEdit features), I'm forced to let the window own that data. It's stored in a TE Handle; However, I get the handle back whenever I need to do any I/O with the data. In all other respects, the CEnsembleData class "owns" the data (e.g., for the spreadsheet row and column styles, as well as the spreadsheet data itself). Other modules access that data through the auspices of the CEnsembleData module. In some cases, I have allowed the data handles to be public, and have passed them, explicitly, to other modules (e.g., CList15), so that the data could be more readily accessed; however, in retrospect, I probably should have provided access methods in the CEnsembleData class so that the requested data could be returned, which would have better "encapsulated" that data. Once you get the "hang" of OO design, I don't think the transition to C++ will be that great…at least I haven't found it to be so. Best of luck, -rich-
#7234From: Spec BowersJul 20, 1993 6:23 PM
Burt, The TCL does not use the Mac List Manager. That's why it doesn't suffer from the many limitations of the List Manager. In the TCL, you'll usually use an ArrayPane which is a Table set up to use an Array. The Table provides all the user interface for a list; the Array provides the data. ArrayPane and Table are two classes that are always subclassed. AppMaker generates a subclass for you that has a GetCellText method. That method accesses the associated Array (or other data structure) for cell (x,y) and returns a string. You don't have two copies of the data; you just have whatever data structure is convenient for you and GetCellText accesses that data structure. There is an initialization method for the list subclass which is where you would normally put the code to set the number of rows of the list. AppMaker generates a call to AddRow to add four rows for testing purposes. Look at our AMReminder example and you'll see a list that is built dynamically at runtime. Spec
#7240From: Burt JohnsonJul 20, 1993 7:44 PM
Thanks for the explanation of the lists. It's good to know that I am only paying the memory price for the list once, and have no problem keeping it in my program in that case. I saw the GetCellText and the initialization code, and believe I understand them. However, the example I saw assumes that the rows are all added at initialization. In my case, at initialization I create an instance of the table, but it is empty. Then, as the program runs, it begins to fill up. My question is how to add rows at that later date from a different part of the program. That is, the instance variable is not global, so what do I refer to when I want to say "x->addrow()"? What is 'x' and where do I get it from if it is not a 'gX' global? I'll look at AMReminder again and see if I can fathom this out from there. I had looked through Rich's book and failed to figure it out, and your manual has no list of classes and methods as I expected. – Burt
#7265From: David JokinenJul 20, 1993 11:09 PM
>>>the instance variable is not global, so what do I refer to when I want to say "x->addrow()"?<<< From within any subclass of CArrayPane, do this: itsArray->AddRow(…your instructions…); From anywhere else, do this: CArrayPane *myArrayPane; // You can only get 'myArrayPane' from whoever created it, // which is tricky if it wasn't you! If AppMaker is using // CDLOGDirector and a DITL resource, you can ask // CDirector::FindViewByID(…DITL index…) from the // relevant DialogDirector. myArrayPane->GetArray()->AddRow(…your instructions…); I suggest you use the first technique. It's cleaner and more general. To do this, subclass CArrayPane for each type of list you want to implement, then either have the class update itself, or create access functions for the different ways you want to effect your list. I have a whole suite of classes for the different types of dynamic lists I maintain.
#7274From: Burt JohnsonJul 21, 1993 1:46 AM
Thanks — that looks like the exact answer I was looking for! – Burt
#7287From: Spec BowersJul 21, 1993 11:40 AM
Burt, > That is, the instance variable is not global, so what do I refer to when > I want to say "x->addrow()"? What is 'x' and where do I get it from if > it is not a 'gX' global? There are two "x"s. One is the list pane (usually a subclass of CArrayPane), the other is its data. We'll call the pane the AddressesList, and we'll call the data the AddressesArray. AppMaker generates an instance variable for the pane. It's in your window class, so within that class you can call (e.g.) AddressesList->AddRow(…). The data for your list you'll normally define in the Data class. In the initialization method for your list pane subclass you'll call AddressesList->SetArray(AddressesArray). As a matter of good programming practice, you normally don't refer directly to another class's instance variables. You instead call a member function (aka method) in that class that accesses its instance variables (aka data members). So in your data class, you'll have functions for manipulating (adding, removing, etc.) the AddressesArray. The TCL uses the Collaborator mechanism to keep the array and the pane in sync. As you add elements to AddressesArray, the TCL automatically adds rows to AddressesList by BroadcastChange/ProviderChanged. Most of your classes will have an instance variable, itsData, that is a pointer to your data class object. If necessary, you'll pass itsData to other classes so that they can store a copy in their own instance variables. You'll call such things as itsData->AddAddress(anAddress). The functions in the data class will access the AddressesArray to add, remove, etc. entries. Spec
#7338From: Burt JohnsonJul 21, 1993 8:59 PM
I finally called a friend today and went over with my program in hand and asked him to help me past this hurdle. As I expected, it turned out to be pretty easy. The key was that when I created the instance of my class (CCard, which is the class I created to handle NuBus cards), I had to pass it pointers to 'this' and each of the two lists I wanted it to be able to call methods for. Once I had those instance handles in my CCard class, my methods merrily did exactly what I expected. 🙂 Now, hopefully I'll be able to make some progress and not have to revert to C just to meet the deadline! – Burt