WELCOME TO THE MVP NIGHT SCHOOL
If you've been struggling with Dolphin's MVP framework then you've come to the right place for help. I am going to be your "tutor" for this term (although I'm sure I'll be abley assisted by other Dolphin veterans as and when necessary). We can start off running the school as as series of question and answer sessions where "students" can pose conceptual or practical questions about areas they find difficult in MVP and I'll try and come up with some answers on a daily (or should I say, "nightly") basis. First of all, though, I'd like to make it clear that I don't welcome shirkers in my class. You can't just sit at the back and chat to members of the opposite sex (there's plenty of time for that in the bar afterwards). If you're here, your going to have to work, so here's your pre-class MVP reading list for this term. When you've read through this lot then we can begin. MVP READING LIST Original Taligent MVP Paper http://www-4.ibm.com/software/developer/library/mvp.html Initial MVP Discusion http://www.object-arts.com/Lib/EducationCentre4/htm/creatingguiapplications. htm Object Arts' Comparison of MVP to other frameworks http://www.object-arts.com/Papers/TwistingTheTriad.PDF MVP Patterns http://www.object-arts.com/EducationCentre/Patterns/MVP.htm http://www.object-arts.com/EducationCentre/Patterns/BasicMVPComponent.htm http://www.object-arts.com/EducationCentre/Patterns/CompositeMVPComponent.ht m Object Arts Tutorials http://www.object-arts.com/EducationCentre/Tutorials/ScribblePad.htm http://www.object-arts.com/EducationCentre/Tutorials/EtchASketch.htm http://www.object-arts.com/EducationCentre/Tutorials/CreatingAnApplication.h tm http://www.object-arts.com/wiki/html/Dolphin/MVPInAMinute.htm Extended Personal Money Tutorial by Louis Sumberg http://www.sirius.com/~lsumberg/PersonalMoney Ian Bartholomew's Tutorials http://www.iandb.org.uk/tutorials.htm Discussion of ValueModels http://www.object-arts.com/EducationCentre/Overviews/ValueModels.htm Best Regards, Andy Bower MVP Night School |
The page about MVP on the Wiki is a revelation. I hadn't found that before.
cheers. "Andy Bower" <[hidden email]> wrote in message news:91ajga$3anbg$[hidden email]... > WELCOME TO THE MVP NIGHT SCHOOL > > If you've been struggling with Dolphin's MVP framework then you've come to > the right place for help. > > I am going to be your "tutor" for this term (although I'm sure I'll be abley > assisted by other Dolphin veterans as and when necessary). We can start off > running the school as as series of question and answer sessions where > "students" can pose conceptual or practical questions about areas they find > difficult in MVP and I'll try and come up with some answers on a daily (or > should I say, "nightly") basis. > > First of all, though, I'd like to make it clear that I don't welcome > shirkers in my class. You can't just sit at the back and chat to members of > the opposite sex (there's plenty of time for that in the bar afterwards). If > you're here, your going to have to work, so here's your pre-class MVP > reading list for this term. When you've read through this lot then we can > begin. > > MVP READING LIST > > Original Taligent MVP Paper > http://www-4.ibm.com/software/developer/library/mvp.html > > Initial MVP Discusion > > htm > > > Object Arts' Comparison of MVP to other frameworks > http://www.object-arts.com/Papers/TwistingTheTriad.PDF > > MVP Patterns > http://www.object-arts.com/EducationCentre/Patterns/MVP.htm > http://www.object-arts.com/EducationCentre/Patterns/BasicMVPComponent.htm > > m > > Object Arts Tutorials > http://www.object-arts.com/EducationCentre/Tutorials/ScribblePad.htm > http://www.object-arts.com/EducationCentre/Tutorials/EtchASketch.htm > http://www.object-arts.com/EducationCentre/Tutorials/CreatingAnApplication.h > tm > http://www.object-arts.com/wiki/html/Dolphin/MVPInAMinute.htm > > Extended Personal Money Tutorial by Louis Sumberg > http://www.sirius.com/~lsumberg/PersonalMoney > > Ian Bartholomew's Tutorials > http://www.iandb.org.uk/tutorials.htm > > Discussion of ValueModels > http://www.object-arts.com/EducationCentre/Overviews/ValueModels.htm > > Best Regards, > > Andy Bower > MVP Night School > > > |
In reply to this post by Andy Bower
Andy:
This is great. Will you post the discussion to the dolphin wiki for future reference? Mike Sent via Deja.com http://www.deja.com/ |
In reply to this post by Andy Bower
I think helping us out with this stuff will help a lot. Here are my
questions that I don't believe are addressed in the docs. 1) What do you do in the presenter in case you have data from multiple models? Do you have to create a supermodel (like a Cindy Crawford :) to add to presenter? 2) I created Shell view with additional controls and saved it as a Shell owning class and gave it a new resource name (instead of the default view). Do I now have to add the methods to the Shell presenter class? If that's the case how do I distinguish between the Default view and my new view when the methods execute? Costas |
In reply to this post by mchean-2
Mike,
> This is great. Will you post the discussion to > the dolphin wiki for future reference? No, someone else can do that. Perhaps a better alternative would be to refer to the newsgroup archive files that Ian B keeps on his site. These are also searchable by DSDN. Best Regards, Andy Bower Dolphin Support |
In reply to this post by Costas Menico-2
Costas,
> 1) What do you do in the presenter in case you have data from multiple > models? Do you have to create a supermodel (like a Cindy Crawford :) > to add to presenter? I imagine that by the phrase "supermodel" you what probably referring to a Facade (Design Patterns p. 185). This is certainly not essential but it might be a typical thing to do. For example, in the development environment, the class SmalltalkSystem is really a Facade onto the various development classes. You'll notice that the singleton instance of SmalltalkSystem is used as the model for most of the development tools. However, as I say, it is not always essential to create such a Facade bringing together several different models just so they can be manipulated by one presenter. Indeed, it is not actually necessary to have a model at all. If you take a look at SmalltalkWorkspace you'll see that it, in fact, has a nil model (actually, this was a rather poor design decision taken in the days of low memory machines to minimise the amount of duplicate text that would have to be stored in the image). Anyway, this does show that the rules can be bent and it is not essential to have a central model if it does not fit your design. So, if you have several different models scattered about your image and you want to create a presenter to manipulate them, what should you do. Well, it is the job of a composite presenter to edit (or just display) the contents of multiple models whether they be owned by a higher level composite model or not. So, create your presenter as a sub class of CompositePresenter. If you don't have an overall model that the presenter should own, then you don't need to override the class side #defaultModel method since the default will simply answer nil. Usually it is the job of the composite presenter's #model: method to take a composite model and split it up into individual component models and hand these over to the the various sub-presenters. In this case, we don't have a composite model so nil will be passed through. However, this is the place to go and locate your individual component models and assign them to the various sub-presenters in your composite. Unfortunately, the above discussion does not apply to the use of Dialog presenters. These do have to have a single composite model on which to work. This is because of the way that Dialog and ValueDialog need to create a buffer to hold the temporary changes made to the model before they are committed by clicking the OK button. In the case of a plain Dialog, an AspectBuffer is used to buffer up write accesses into a copy of the original model before they are committed. In a case of a ValueDialog, a ValueBuffer holds on to the replacement model value until the Dialog is OKed. In both situations it is necessary to have a central unified location for all of your model data (ie a composite model) so that the buffering schemes can work. > 2) I created Shell view with additional controls and saved it as a > Shell owning class and gave it a new resource name (instead of the > default view). Do I now have to add the methods to the Shell > presenter class? If that's the case how do I distinguish between the > Default view and my new view when the methods execute? I'm not sure what you're saying here. Are you saying that you are modifying the basic Shell class supplied with the system or have you created your own application specific subclass? Anyway, you should certainly do the latter.Whenever you are creating a new application window you should be creating a new presenter class. In this case, since you have decided that the application window should be a top-level shell, you'll probably want to create a subclass of Shell (call it, MyShell) where you write your application specific methods. If you are saying that you have added additional views over and above those available in the default view and these require additional presenters to be added to your MyShell class then typically this is an indication that you actually require a new subclass of MyShell. You should need to have to try and distinguish the functionality of a presenter based on what is in the view Best Regards Andy Bower MVP Night School |
In reply to this post by Costas Menico-2
Costas,
> 1) What do you do in the presenter in case you have data from multiple > models? Do you have to create a supermodel (like a Cindy Crawford :) > to add to presenter? As you found my last demo useful I've done another to illustrate this. It's similar to the last except that we have two classes unconnected to the main Presenter which are used to provide the aspect models for the presenter's sub views. The main Presenter, as Andy's reply discusses, doesn't have a model at all and defaults to nil. This means that the Presenters #model: method never gets called and we have to connect the sub presenters somewhere else - I've used #onViewOpened - but there are alternatives. I've added an extra bit to show one way that the main Presenter can be notified when _any of the aspects change. You will need the Transcript open to see it's output. http://www.iandb.org.uk/~iandb/demo2.zip One other thing that should be mentioned - We have been using a line like aTextPresenter model: ((myModel aspectValue: #fred) aspectTriggers: #fredChanged). This format is needed if updates can occur in both directions - editing the view needs to update the fred in MyModel _and_ changing fred itself needs to update the view. If there is no possibility of the model's fred value being changed programatically - if it can _only be changed by editing the view - then there is no need to notify anybody when the model changes. You could then just use the following and not bother triggering the event from the model. aTextPresenter model: (myModel aspectValue: #fred) So in demo2 you could change the #onViewOpened method to read as below and, because I deliberately avoided updating lastName programmatically, it should not affect the way the demo works. onViewOpened super onViewOpened. firstNamePresenter model: ((name aspectValue: #firstName) aspectTriggers: #firstNameChanged). lastNamePresenter model: (name aspectValue: #lastName). cityPresenter model: ((address aspectValue: #city) aspectTriggers: #cityChanged). Ian - I hope teacher doesn't mind me interrupting the class. Should I have put my hand up first?? |
More MVP questions<g>.
I have a long running process. This process has a ListModel which is the model for a ListPresenter. What I want is for the main windows (which is a Shell containing a List Presenter to be updated as the long running process runs. The long running process simply adds periodically to the list. The idea is to have a history log in the list where the process updates what is happening. In essence the model provided by this process is a constantly updated collection of messages. This is basically a variation of a standard progress indicator type situation. When I override the viewOpened method in my ShellPresenter subclass to run this process, the window does NOT open until the process is over. I then tried [self model run] fork in the viewOpened method and that got the desired result. Here are my questions. 1. Is the fork approach in the viewOpened method a reasonable one? Almost all the MPD examples I have seen tend to be examples where the user causes things to happen. Not where the window opens and some processing immediately starts. 2. The normal ListPresenter keeps the selection at the top as items are added. I want to scroll this list as the items are added so that the bottom is always in view. There seem to be two ways to do this without breaking MPD separation (1) Bring some aspect of the loop that takes place in the process into the presenter so that on each iteration we can send the sub presenter the appropriate messages to scroll the list as needed. (2) If I feel strongly, that the iteration in the process is really part of the model in some intrinsic way and does NOT belong in the root presenter, then I need to create a different view on ListPresenter (instead of the default) that will automatically scroll the list as needed. (3) break MPD separation by passing the presenter into the model to allow the model to handle scrolling. This seems clearly bad. At any rate I was just wondering if these seem reasonable to people who have more experience with MPD than I do. I have used WindowBuilder and while there is some similarity it is also quite different<g>. I just want to make sure, as it were, that I am not missing some obviously simpler way to do this. As usual, thanks to everyone for their help. And to Object Arts for Dolphin. Dolphin is going to be handling some mission critical stuff for me. The core classes are done. Now I have to get some kind of simple UI on them<g> On Fri, 15 Dec 2000 13:39:02 -0000, "Ian Bartholomew" <[hidden email]> wrote: >Costas, > >> 1) What do you do in the presenter in case you have data from multiple >> models? Do you have to create a supermodel (like a Cindy Crawford :) >> to add to presenter? > |
Eric,
> 1. Is the fork approach in the viewOpened method a reasonable one? > Almost all the MPD examples I have seen tend to be examples where the > user causes things to happen. Not where the window opens and some > processing immediately starts. Yes, forking is the way to do this as you want the model to be running at the same time (in effect) as the Shell. However you have to be a bit careful about how you do this as it is very easy to fork off a process that never terminates. In your example you could end up with the Model running after the main Shell had been closed which might not be what you want. There are a number of ways to do this but, IMHO, one of the safest is to put the forked process into the model, where it probably belongs, and control this forked process externally, from the shell. > 2. The normal ListPresenter keeps the selection at the top as items > are added. I want to scroll this list as the items are added so that > the bottom is always in view. Just use normal MVP behaviour - The model maintains a list of all the log events in a simple OrderedCollection. When an addition is made to this list the model triggers and event saying that the list has been changed. The associated presenter has registered for this event and knows when it occurs that it must update it's ListPresenter. AModel>>logEvent: anLogEvent logEvents addLast: aLogEvent. self trigger: #logChanged AModel>>logEvents ^logEvents copy AShellPresenter>>createSchematicWiring aModel when: #logChanged send: #onLogChanged to: self AShellPresenter>>onLogChanged aListPresenter list: aModel logEvents; selection: aCollection last If you want to ensure that the collection of logged events remains a bit more private (i.e. you really don't want to provide an accessor method in the model) then you can pass the logEvents collection as part of the triggered event. AModel>>logEvent: anLogEvent logEvents addLast: aLogEvent. self trigger: #logChanged: with: logEvents copy AShellPresenter>>createSchematicWiring aModel when: #logChanged: send: #onLogChanged: to: self AShellPresenter>>onLogChanged: aCollection aListPresenter list: aCollection; selection: aCollection last You could also do something similar but sharing a single ListModel between the Shell and the Model. This means you wouldn't have to worry about passing the logEvents collections about, just the fact that it had changed. OK. An example of the two concepts above. NB. I don't know how your model class works so the following just checks every 1.5 seconds and add the current time, if contains an odd number of seconds, to the log. Model subclass LogModel with two instVars - process and logEvents LogModel>>start logEvents := OrderedCollection new. process := [[ self checkForUpdates. Processor sleep: 1500] repeat ] forkAt: Processor userBackgroundPriority This is sent by the Shell when it wants the model to start logging events. It forks off a process that checks to see if any new events have occurred, dealing with them if they have, and then goes to sleep for 1.5 seconds. I've used #forkAt: instead of a simple #fork as it tells the processor that if something more important comes up, the UI needs updating for example, then this process can be interrupted and the higher priority process run. It doesn't make a lot of difference in this case but if the #checkForUpdates method was slow then it could make the application seem much more responsive. LogModel>>stop process notNil ifTrue: [ process terminate. process := nil] Sent by the Shell when it wants the process to stop sending log updates. If this is not called, and the Shell tries as hard as it can to make sure it is, then you can end up with the process running after everything interested in the log has disappeared. Even removing all references to the LogModel instance will not help as the fact that the process' block is referencing itself prevents normal garbage collection and finalisation. LogModel>>checkForUpdates | update | update := self getUpdate. update notNil ifTrue: [ logEvents addLast: update. self trigger: #logUpdated: with: logEvents copy] This just checks to see if an update has occurred and, if so, adds the new update and triggers an event passing the new list as a parameter. It passes a copy for two reasons - - Security - it prevents any external changes to a privately owned object - It provides a different object each time (even though the contents of the object are the same). This prevents a peculiarity of the Dolphin ListPresenter where it won't update a list if it thinks the list hasn't changed. LogModel>>getUpdate ^Time now asSeconds odd ifTrue: [Time now] Just a way of generating random updates. This answers the current time if it contains an odd number of seconds or nil (via the implicit ifFalse: []) if even. That's it for the LogModel. This can be tested in a workspace - lm := LogModel new. lm when: #logUpdated: send: #bell to: Sound. lm start You should now start hearing occasional beeps from your system speaker. To show how the process continues, even though we have cleared our only reference to the LogModel, evaluate the following and notice that the beeps keep on beeping. lm := nil As we can't now send "lm stop" (we have set lm to nil) we have to stop the process by using the image to locate the instance of LogModel and stopping it that way LogModel allInstances do: [:each | each stop] The Presenter is quite straightforward. The associated view will contain one ListPresenter.Default view named "view" and the class will have one instance variable named "listPresenter" Shell subclass Log Class method - Log class>>defaultModel ^LogModel new Instance methods - Log>>createComponents super createComponents. listPresenter := self add: ListPresenter new name: 'list' Log>>createSchematicWiring super createSchematicWiring. self model when: #logUpdated: send: #onLogUpdated: to: self As normal. We connect to the ListPresenter MVP in the listPresenter instVar and also tell our model to let us know when the #logUpdated: event occurs. [Diversion] When we triggered the #logUpdated: event in our model we passed an argument (the current log collection) with it. The #when:send:to: statement above implicitly tells Dolphin to pass the argument on, so the collection ends up being passed as the argument to the #onLogUpdated: method. You can also pass multiple arguments (as below) and Dolphin will pass them on for you but the method signatures _must match. If the trigger indicates two arguments (two embedded colons) then the target selector must also have two colons. If not then some arguments will be lost in transition. (x) self trigger: #arg1:arg2: with: 'argOne' with: 'argTwo' -------- (y) x when: #arg1:arg2: send: #onArg1:arg2: to self -------- (y) onArg1: arg1String arg2: arg2String arg1String will equal 'argOne' arg2String witll equal 'argTwo' Log>>onViewOpened super onViewOpened. self beFinalizable. self model start When the Log shell is opened we want the model to be started and this is the easiest place to do it. The #beFinalizable reference is just a piece of insurance here that tells the VM that as a final check, before this instance is destroyed, it should send #finalize to the instance so that it can tidy up. This usually lets the instance destroy any operating system resources that it may have acquired but in this case we will want to make sure the process in the model is stopped. It won't be necessary if the Log shell was closed normally but will only be used, as insurance, if for some reason the Log shell closed abnormally Log>>onViewClosed super onViewClosed. self finalize Log>>finalize self model stop The two ways of ensuring the model is stopped. Normally #onViewClosed is used and #finalize is called that way. If, for some reason, #onViewClosed is nor run then the default finalization that we asked for will call #finalize directly. Log>>onLogUpdated: aCollection listPresenter list: aCollection; selection: aCollection last The model has triggered the event to say it has been updated, and passed the updated collection as well. We end up here and tell the ListPresenter (the ListModel associated with the ListPresenter MVP) the new collection. The scrolling is achieved by selecting the new log entry - by default the last in the list. If we could not guarantee that it would be the last in the list (the list was sorted for example) we could pass the new log entry as part of the triggering, in addition to the complete list, and we could still select it here. That's it. Attached is a D4 package (text so users of 2/3 will still be able to look at it even if they can't load it) containing a working version of the above - which was typed in and may contain the occasional code typo? Ian begin 666 Log.pac M?"!P86-K86=E('P-"G!A8VMA9V4@.CT@4&%C:V%G92!N86UE.B G3&]G)RX- M"G!A8VMA9V4@<&%X5F5R<VEO;CH@,#L-"@EB87-I8T-O;6UE;G0Z("=,;V<@ M<VAO=R<N#0H-"G!A8VMA9V4@8F%S:6-086-K86=E5F5R<VEO;CH@)R<N#0H- M"B)!9&0@=&AE('!A8VMA9V4@<V-R:7!T<R(-"@T*(D%D9"!T:&4@8VQA<W,@ M;F%M97,L(&QO;W-E(&UE=&AO9"!N86UE<RP@9VQO8F%L(&YA;65S+"!R97-O M=7)C92!N86UE<R(-"G!A8VMA9V4@8VQA<W-.86UE<PT*"6%D9#H@(TQO9SL- M"@EA9&0Z("-,;V=-;V1E;#L-"@EY;W5R<V5L9BX-"@T*<&%C:V%G92!M971H M;V1.86UE<PT*"7EO=7)S96QF+@T*#0IP86-K86=E(&=L;V)A;$YA;65S#0H) M>6]U<G-E;&8N#0H-"G!A8VMA9V4@<F5S;W5R8V5.86UE<PT*"7EO=7)S96QF M+@T*#0HB0FEN87)Y($=L;V)A;"!.86UE<R(-"G!A8VMA9V4@8FEN87)Y1VQO M8F%L3F%M97,Z("A3970@;F5W#0H)>6]U<G-E;&8I+@T*(E)E<V]U<F-E($YA M;65S(@T*<&%C:V%G92!A;&Q297-O=7)C94YA;65S.B H4V5T(&YE=PT*"6%D M9#H@(TQO9R M/B G1&5F875L="!V:65W)SL-"@EY;W5R<V5L9BDN#0H-"B)! M9&0@=&AE('!R97)E<75I<VET92!N86UE<R(-"G!A8VMA9V4@<V5T4')E<F5Q M=6ES:71E<SH@*$ED96YT:71Y4V5T(&YE=PT*"6%D9#H@)T1O;'!H:6XG.PT* M"7EO=7)S96QF*2X-"@T*<&%C:V%G92$-"@T*(D-L87-S($1E9FEN:71I;VYS M(B$-"@T*36]D96P@<W5B8VQA<W,Z("-,;V=-;V1E; T*"6EN<W1A;F-E5F%R M:6%B;&5.86UE<SH@)W!R;V-E<W,@;&]G179E;G1S)PT*"6-L87-S5F%R:6%B M;&5.86UE<SH@)R<-"@EP;V]L1&EC=&EO;F%R:65S.B G)PT*"6-L87-S26YS M=&%N8V5687)I86)L94YA;65S.B G)R$-"E-H96QL('-U8F-L87-S.B C3&]G M#0H):6YS=&%N8V5687)I86)L94YA;65S.B G;&ES=%!R97-E;G1E<B<-"@EC M;&%S<U9A<FEA8FQE3F%M97,Z("<G#0H)<&]O;$1I8W1I;VYA<FEE<SH@)R<- M"@EC;&%S<TEN<W1A;F-E5F%R:6%B;&5.86UE<SH@)R<A#0HB3&]O<V4@365T M:&]D<R(A#0H-"B)%;F0@;V8@<&%C:V%G92!D969I;FET:6]N(B$-"@T*#0H- M"DQO9TUO9&5L(&-O;6UE;G0Z("<G(0T*#0I,;V=-;V1E;"!G=6ED.B H1U5) M1"!F<F]M4W1R:6YG.B G>S9%1C8Q0T$U+4-%1#(M-#,T02U!149$+41#03 R M1D$R.$)!,7TG*2$-"@T*(4QO9TUO9&5L(&-A=&5G;W)I97-&;W)#;&%S<R%5 M;F-L87-S:69I960A("$-"B%,;V=-;V1E;"!M971H;V1S1F]R(0T*#0IC:&5C M:T9O<E5P9&%T97,-"@E\('5P9&%T92!\#0H)=7!D871E(#H]('-E;&8@9V5T M57!D871E+@T*"75P9&%T92!I<TYI; T*"0EI9D9A;'-E.B!;#0H)"0EL;V=% M=F5N=',@861D3&%S=#H@=7!D871E+@T*"0D)<V5L9B!T<FEG9V5R.B C;&]G M57!D871E9#H@=VET:#H@;&]G179E;G1S(&-O<'E=#0H)"0D)(0T*#0IG9715 M<&1A=&4-"@DB4VAO=6QD(&%N<W=E<B!A;B!O8FIE8W0@;W(@;FEL(&EF('1H M97)E(&%R92!N;R!U<&1A=&5S(@T*"5Y4:6UE(&YO=R!A<U-E8V]N9',@;V1D M(&EF5')U93H@6U1I;64@;F]W70T*"0D)"2$-"@T*<W1A<G0-"@EL;V=%=F5N M=',@.CT@3W)D97)E9$-O;&QE8W1I;VX@;F5W+@T*"7!R;V-E<W,@.CT@6UL- M"@D)<V5L9B!C:&5C:T9O<E5P9&%T97,N#0H)"5!R;V-E<W-O<B!S;&5E<#H@ M,34P,%T@<F5P96%T72!F;W)K070Z(%!R;V-E<W-O<B!U<V5R0F%C:V=R;W5N M9%!R:6]R:71Y(0T*#0IS=&]P#0H)<')O8V5S<R!N;W1.:6P-"@D):694<G5E M.B!;#0H)"0EP<F]C97-S('1E<FUI;F%T92X-"@D)"7!R;V-E<W,@:7-.:6Q= M(2 A#0HA3&]G36]D96P@8V%T96=O<FEE<T9O<CH@(V-H96-K1F]R57!D871E M<R$J+75N8VQA<W-I9FEE9"%P=6)L:6,A("$-"B%,;V=-;V1E;"!C871E9V]R M:65S1F]R.B C9V5T57!D871E(2HM=6YC;&%S<VEF:65D(7!U8FQI8R$@(0T* M(4QO9TUO9&5L(&-A=&5G;W)I97-&;W(Z("-S=&%R="$J+75N8VQA<W-I9FEE M9"%P=6)L:6,A("$-"B%,;V=-;V1E;"!C871E9V]R:65S1F]R.B C<W1O<"$J M+75N8VQA<W-I9FEE9"%P=6)L:6,A("$-"@T*#0H-"DQO9R!C;VUM96YT.B G M)R$-"@T*3&]G(&=U:60Z("A'54E$(&9R;VU3=')I;F<Z("=[-34Q,# R-40M M,D8P."TT-$4X+3E%1CDM03 Y0S$V.# V0D%#?2<I(0T*#0HA3&]G(&-A=&5G M;W)I97-&;W)#;&%S<R%5;F-L87-S:69I960A("$-"B%,;V<@;65T:&]D<T9O M<B$-"@T*8W)E871E0V]M<&]N96YT<PT*"7-U<&5R(&-R96%T94-O;7!O;F5N M=',N#0H);&ES=%!R97-E;G1E<B Z/2!S96QF(&%D9#H@3&ES=%!R97-E;G1E M<B!N97<@;F%M93H@)VQI<W0G(0T*#0IC<F5A=&538VAE;6%T:6-7:7)I;F<- M"@ES=7!E<B!C<F5A=&538VAE;6%T:6-7:7)I;F<N#0H)<V5L9B!M;V1E;"!W M:&5N.B C;&]G57!D871E9#H@<V5N9#H@(V]N3&]G57!D871E9#H@=&\Z('-E M;&8A#0H-"F9I;F%L:7IE#0H)<V5L9B!M;V1E;"!S=&]P(0T*#0IO;DQO9U5P M9&%T960Z(&%#;VQL96-T:6]N#0H);&ES=%!R97-E;G1E<B -"@D);&ES=#H@ M84-O;&QE8W1I;VX[#0H)"7-E;&5C=&EO;CH@84-O;&QE8W1I;VX@;&%S="$- M"@T*;VY6:65W0VQO<V5D#0H)<W5P97(@;VY6:65W0VQO<V5D+@T*"7-E;&8@ M9FEN86QI>F4A#0H-"F]N5FEE=T]P96YE9 T*"7-U<&5R(&]N5FEE=T]P96YE M9"X-"@ES96QF(&)E1FEN86QI>F%B;&4N#0H)<V5L9B!M;V1E;"!S=&%R= T* M#0HA("$-"B%,;V<@8V%T96=O<FEE<T9O<CH@(V-R96%T94-O;7!O;F5N=',A M*BUU;F-L87-S:69I960A<'5B;&EC(2 A#0HA3&]G(&-A=&5G;W)I97-&;W(Z M("-C<F5A=&538VAE;6%T:6-7:7)I;F<A*BUU;F-L87-S:69I960A<'5B;&EC M(2 A#0HA3&]G(&-A=&5G;W)I97-&;W(Z("-F:6YA;&EZ92$J+75N8VQA<W-I M9FEE9"%P=6)L:6,A("$-"B%,;V<@8V%T96=O<FEE<T9O<CH@(V]N3&]G57!D M871E9#HA*BUU;F-L87-S:69I960A<'5B;&EC(2 A#0HA3&]G(&-A=&5G;W)I M97-&;W(Z("-O;E9I97=#;&]S960A*BUU;F-L87-S:69I960A<'5B;&EC(2 A M#0HA3&]G(&-A=&5G;W)I97-&;W(Z("-O;E9I97=/<&5N960A*BUU;F-L87-S M:69I960A<'5B;&EC(2 A#0H-"B%,;V<@8VQA<W,@;65T:&]D<T9O<B$-"@T* M9&5F875L=$UO9&5L#0H)7DQO9TUO9&5L(&YE=R$@(0T*(4QO9R!C;&%S<R!C M871E9V]R:65S1F]R.B C9&5F875L=$UO9&5L(2HM=6YC;&%S<VEF:65D(7!U M8FQI8R$@(0T*#0H@#0HB0FEN87)Y($=L;V)A;',B(0T*#0HB4F5S;W5R8V5S M(B$-"@T**%)E<V]U<F-E261E;G1I9FEE<B!C;&%S<SH@3&]G(&YA;64Z("=$ M969A=6QT('9I97<G*2!A<W-I9VXZ("A/8FIE8W0@9G)O;4)I;F%R>5-T;W)E M0GET97,Z#0HC6S,S(#@S(#@T(#8V(#,R(#0X(#,R(#<P(#(@,3(@," Q(# @ M," P(#@V(#$P-2 Q,#$@,3$Y(#@R(#$P,2 Q,34@,3$Q(#$Q-R Q,30@.3D@ M,3 Q(# @," P(# @,30@,2 S-B P(#@S(#@T(#8V(#@R(#$P,2 Q,34@,3$Q M(#$Q-R Q,30@.3D@,3 Q(#@S(#@T(#8V(#8V(#$R,2 Q,38@,3 Q(#8U(#$Q M-" Q,30@.3<@,3(Q(#8U(#DY(#DY(#$P,2 Q,34@,3$U(#$Q,2 Q,30@.# @ M,3$T(#$Q,2 Q,C @,3(Q(# @," P(# @-30@," Y(# @-C8@,3(Q(#$Q-B Q M,#$@-C4@,3$T(#$Q-" Y-R Q,C$@,38Q(#0@," P(#,S(#@S(#@T(#8V(#,R M(#0X(#,R(#<X(#@@,3(@," Q," P(# @," X,R X-" V-B X-B Q,#4@,3 Q M(#$Q.2 X," Q,30@,3$Q(#$R," Q,C$@," P(# @," W." R(#$S(# @,2 P M(# @," X,R X-" V-B V-R Q,#@@.3<@,3$U(#$Q-2 X," Q,30@,3$Q(#$R M," Q,C$@," P(# @," U-" P(#8@," X,R Q,38@,3$T(#$P-2 Q,3 @,3 S M(#<@," P(# @-C@@,3$Q(#$P." Q,3(@,3 T(#$P-2 Q,3 @,30V(# @," P M(#D@," P(# @.#,@,3 T(#$P,2 Q,#@@,3 X(#@V(#$P-2 Q,#$@,3$Y(#,X M(# @-2 P(#8U(#$Q-" Q,30@.3<@,3(Q(#(T(# @," P(# @," P(# @," P M(# @," Q.30@," P(# @,B P(# @," Q(# @,34X(#$@,2 P(#(@," Y-B P M(# @," P(# @," P(# @," P(# @-B R(#4@," X," Q,3$@,3 U(#$Q," Q M,38@," P(# @," R,34@,2 P(# @,3DU(#$@," P(#<@,B P(# @," P(# @ M," P(# @," P(# @," P(# @," P(# @," P(# @," P(#$T(#(@,C8@," X M,R X-" V-B W,R Q,# @,3 Q(#$Q," Q,38@,3 U(#$Q-B Q,C$@-C@@,3 U M(#DY(#$Q-B Q,#4@,3$Q(#$Q," Y-R Q,30@,3(Q(#@P(#$Q-" Q,3$@,3(P M(#$R,2 P(# @," P(#$R,B P(# @," P(# @," P(#$V," P(# @," Q-#8@ M," P(# @,3@@," P(# @-S,@,3 P(#$P,2 Q,3 @,3$V(#$P-2 Q,38@,3(Q M(#8X(#$P-2 Y.2 Q,38@,3 U(#$Q,2 Q,3 @.3<@,3$T(#$R,2 Q.30@," P M(# @,B P(# @," Y," P(# @," P(# @," P(#$R,B P(# @," P(# @," P M(#$V," P(# @," Q-#8@," P(# @-R P(# @," W-B Q,#4@,3$U(#$Q-B V M-B Q,3$@,3(P(#$Y-" P(# @," Q-R P(# @," P(# @," P(#DV(# @," P M(#$Y-" P(# @," R(# @," P(#4T(# @,3(@," W-B Y-R Q,30@,3 S(#$P M,2 W,R Q,3 @,3$V(#$P,2 Q,#,@,3 Q(#$Q-" T(# @," P(#$@,2 T.2 V M." Q(#0@," P(#DV(#$@," P(#<P(#,@.2 P(#(@," P(# @-S8@,3 U(#$Q M-2 Q,38@-S<@,3$Q(#$P," Q,#$@,3 X(# @," P(# @,30@,B Q." P(#@S M(#@T(#8V(#8W(#$Q,2 Q,#@@,3 X(#$P,2 Y.2 Q,38@,3 U(#$Q,2 Q,3 @ M.# @,3$T(#$Q,2 Q,C @,3(Q(# @," P(# @,3(R(# @," P(# @," P(# @ M,38P(# @," P(#$T-B P(# @," Q-R P(# @," W.2 Q,30@,3 P(#$P,2 Q M,30@,3 Q(#$P," V-R Q,3$@,3 X(#$P." Q,#$@.3D@,3$V(#$P-2 Q,3$@ M,3$P(#$Y-" P(# @," P(# @," P(# @," P(# @-B P(#(P(# @-S,@,3 P M(#$P,2 Q,3 @,3$V(#$P-2 Q,38@,3(Q(#@S(#$P,2 Y-R Q,30@.3D@,3 T M(#@P(#$Q,2 Q,#@@,3 U(#DY(#$R,2 P(# @," P(# @," P(# @," P(# @ M," W(# @," P(# @," P(# @," P(# @," P(# @," P(# @," P(# @," P M(# @," Q-S@@,2 P(# @-" P(# @," R-#0@,3 P(#(R-R Q,3D@,3(R(# @ M," P(# @," P(# @,38P(# @," P(#$T-B P(# @," Q-R P(# @," V-B Y M-R Q,34@,3 U(#DY(#<V(#$P-2 Q,34@,3$V(#8U(#DX(#$Q-2 Q,38@,3$T M(#DW(#DY(#$Q-B Q(# @," P(#,R(# @," P(#8@,2 Q-2 P(#<W(#$P,2 Q M,34@,3$U(#DW(#$P,R Q,#$@.#,@,3 Q(#$Q,R Q,3<@,3 Q(#$Q," Y.2 Q M,#$@," P(# @," R-3 @,2 P(# @," P(# @," Q-B R(# @," Q.30@," P M(# @,B P(# @," V(#,@,3$@," W-R Q,#$@,3$U(#$Q-2 Y-R Q,#,@,3 Q M(#@S(#$P,2 Q,3 @,3 P(# @," P(# @,30@,2 Q-" P(#@S(#@T(#8V(#@S M(#$R,2 Q,#D@.3@@,3$Q(#$P." X," Q,30@,3$Q(#$R," Q,C$@," P(# @ M," Q-#8@," P(# @,38@," P(# @.3D@,3$T(#$P,2 Y-R Q,38@,3 Q(#8U M(#$Q-B U." Q,#$@,3(P(#$Q-B Q,#$@,3$P(#$Q-B U." Q.30@," P(# @ M,B P(# @," R-#(@," P(# @," P(# @," S,2 P(# @," Q,2 P(# @," R M-#(@," P(# @," P(# @," Q,C4@,2 P(# @.34@,2 P(# @.38@,2 P(# @ M,C$P(#(@," P(# @," P(# @,C4P(#(@," P(# @," P(# @,30V(# @," P M(#$W(# @," P(#$P-" Q,3$@,3$T(#$P-2 Q,C(@,3$Q(#$Q," Q,38@.3<@ M,3 X(#8Y(#$R," Q,38@,3 Q(#$Q," Q,38@-3@@,3DT(# @," P(#$@," P M(# @,2 P(# @," Y-B Q(# @," V(#$@,34@," X-R W,R W." V." W.2 X M-R X," W-B V-2 V-R V.2 W-R V.2 W." X-" P(# @," P(#4T(# @.2 P M(#8V(#$R,2 Q,38@,3 Q(#8U(#$Q-" Q,30@.3<@,3(Q(#0T(# @," P(#0T M(# @," P(# @," P(# @,2 P(# @," R-34@,C4U(#(U-2 R-34@,C4U(#(U M-2 R-34@,C4U(#(U-2 R-34@,C4U(#(U-2 R-34@,C4U(#(U-2 R-34@,34@ M," P(# @-2 P(# @," R,#4@," P(# @,3@P(# @," P(#(U," Q(# @," P M(# @," P(#$V(#(@," P(#0X(#(@," P(#(T,B P(# @," P(# @," P(#$Y M,R P(# @," Q.3,@," P(# @," P(# @," Q.2 P(# @," Q-#8@," P(# @ M-" P(# @," Q,#@@,3 U(#$Q-2 Q,38@," P(# @," P(# @," P(# @," P M(# @," P(# @," P(# @," P(#$@," P(# @," P(# @," P(# @," P(# @ M," P(# @," P(# @," Q-#8@,B P(# @," P(# @," R-3 @,2 P(# @," P M(# @," Q-B R(# @," Q.30@," P(# @,B P(# @," R,3 @,B P(# @," P M(# @," P(#,@," P(#$Y-" P(# @," R(# @," P(#(T,B P(# @," P(# @ M," P(#$Q(# @," P(#$Q(# @," P(#(T,B P(# @," P(# @," P(#(Q-2 Q M(# @," Q.34@,2 P(# @.38@," P(# @,C$P(#(@," P(# @," P(# @,C4P M(#(@," P(# @," P(# @,30V(# @," P(#@@," P(# @,3 Y(#$P,2 Q,3 @ M,3$W(#8V(#DW(#$Q-" U." Q.30@," P(# @,2 P(# @," P(# @," P(#DV M(# @," P(#$T-B S(# @," P(# @," P(#$W." S(# @," T-" P(# @," T M-" P(# @," P(# @," P(# @," P(# @,C4U(#(U-2 R-34@,C4U(#(U-2 R M-34@,C4U(#(U-2 R-34@,C4U(#(U-2 R-34@,C4U(#(U-2 R-34@,C4U(#4@ M," P(# @-2 P(# @," R-# @," P(# @,C,P(# @," P(#(U," Q(# @," P M(# @," P(#$V(#(@," P(#$Y-" P(# @," Q(# @," P(#DV(#$@," P(#(R M-" S(# @," P(# @," P(#$Y(# @," P(#<P(#4@-" P(#,@," P(# @-S,@ M.3D@,3$Q(#$Q," P(# @," P(# @," P(# @,38@," P(# @,30@,B Q-R P M(#@S(#@T(#8V(#@S(#$P-2 Q,3 @,3 S(#$P." Q,#$@,3$V(#$Q,2 Q,3 @ M.# @,3$T(#$Q,2 Q,C @,3(Q(# @," P(# @-S@@,B Q,R P(#$@," P(# @ M.#,@.#0@-C8@-C<@,3 X(#DW(#$Q-2 Q,34@.# @,3$T(#$Q,2 Q,C @,3(Q M(# @," P(# @-30@," V(# @.#,@,3$V(#$Q-" Q,#4@,3$P(#$P,R W(# @ M," P(#8X(#$Q,2 Q,#@@,3$R(#$P-" Q,#4@,3$P(#$X(#$@," P(#(T(# @ M," P(#<S(#$P.2 Y-R Q,#,@,3 Q(#@R(#$P,2 Q,#@@.3<@,3$V(#$P-2 Q M,3@@,3 Q(#<P(#$P-2 Q,#@@,3 Q(#<V(#$Q,2 Y.2 Y-R Q,38@,3$Q(#$Q M-" Q-" Q(#$T(# @.#,@.#0@-C8@.#,@,3(Q(#$P.2 Y." Q,3$@,3 X(#@P M(#$Q-" Q,3$@,3(P(#$R,2 P(# @," P(#$X(#$@," P(#<@," P(# @.3D@ M,3$W(#$Q-" Q,30@,3 Q(#$Q," Q,38@,3@@,2 P(# @,3,@," P(# @.#,@ M,3 T(#$P,2 Q,#@@,3 X(#@V(#$P-2 Q,#$@,3$Y(#0V(#$P-2 Y.2 Q,3$@ M,30@,B S,2 P(#@S(#@T(#8V(#8Y(#$R," Q,38@,3 Q(#$Q-" Q,3 @.3<@ M,3 X(#@R(#$P,2 Q,34@,3$Q(#$Q-R Q,30@.3D@,3 Q(#<V(#$P-2 Y." Q M,30@.3<@,3$T(#$R,2 X," Q,30@,3$Q(#$R," Q,C$@," P(# @," Q." Q M(# @," Q-B P(# @," Q,# @,3$Q(#$P." Q,3(@,3 T(#$P-2 Q,3 @,3 P K(#$Q-" T." T." U,B T-B Q,# @,3 X(#$P." P(# @," P72DA#0H-"@`` ` end |
In reply to this post by Eric Langjahr
"Eric Langjahr" <[hidden email]> wrote in message
news:[hidden email]... > I have used > WindowBuilder and while there is some similarity it is also quite > different<g>. I realize it is best to be familiar with both Dolphin and WindowBuilder to do this, but I would like to invite everyone to make specific suggestions with regard to what WindowBuilder provides which would be a great convenience to us if a similar feature were present in Dolphin. (It is amazing how many such things, when well articulated, magically appear in Dolphin!) (Brevity! Pithy, short sentences! That's my motto.) -- Frank [hidden email] I did not have sex with that woman! |
In reply to this post by Eric Langjahr
Well I thought about this some more.
Well I guess the simpler approach would be to have my model trigger an event which basically is a history update. Have my Presenter register an interest in this event, and have the presenter send messages accordingly to the ListPresenter which it owns to control how the ListPresenter behaves. That keeps the model clean, only communicating by triggering events, and puts all the app specific logic in the 'master' presenter (the shell), and allows the shell to customize how the app works. I guess I abandoned this approach before realizing the fork concept worked. But I am going to try this and see. On Sat, 20 Jan 2001 00:31:12 -0800, Eric Langjahr <[hidden email]> wrote: >More MVP questions<g>. > >I have a long running process. This process has a ListModel which is >the model for a ListPresenter. > >What I want is for the main windows (which is a Shell containing a >List Presenter to be updated as the long running process runs. > >The long running process simply adds periodically to the list. > >The idea is to have a history log in the list where the process >updates what is happening. In essence the model provided by this >process is a constantly updated collection of messages. This is >basically a variation of a standard progress indicator type situation. > >When I override the viewOpened method in my ShellPresenter subclass >to run this process, the window does NOT open until the process is >over. > >I then tried [self model run] fork > >in the viewOpened method and that got the desired result. > >Here are my questions. > >1. Is the fork approach in the viewOpened method a reasonable one? >Almost all the MPD examples I have seen tend to be examples where the >user causes things to happen. Not where the window opens and some >processing immediately starts. > >2. The normal ListPresenter keeps the selection at the top as items >are added. I want to scroll this list as the items are added so that >the bottom is always in view. > >There seem to be two ways to do this without breaking MPD separation > > (1) Bring some aspect of the loop that takes place in the process >into the presenter so that on each iteration we can send the sub >presenter the appropriate messages to scroll the list as needed. > > (2) If I feel strongly, that the iteration in the process is really >part of the model in some intrinsic way and does NOT belong in the >root presenter, then I need to create a different view on >ListPresenter (instead of the default) that will automatically scroll >the list as needed. > > (3) break MPD separation by passing the presenter into the model to >allow the model to handle scrolling. This seems clearly bad. > > At any rate I was just wondering if these seem reasonable to people >who have more experience with MPD than I do. I have used >WindowBuilder and while there is some similarity it is also quite >different<g>. > > I just want to make sure, as it were, that I am not missing some >obviously simpler way to do this. > > As usual, thanks to everyone for their help. And to Object Arts for >Dolphin. Dolphin is going to be handling some mission critical stuff >for me. The core classes are done. Now I have to get some kind of >simple UI on them<g> > > >On Fri, 15 Dec 2000 13:39:02 -0000, "Ian Bartholomew" ><[hidden email]> wrote: > >>Costas, >> >>> 1) What do you do in the presenter in case you have data from multiple >>> models? Do you have to create a supermodel (like a Cindy Crawford :) >>> to add to presenter? >> |
In reply to this post by Eric Langjahr
Boy this seems to be just super flexible. Every time I see a way to
do this, I see a simpler way that should work. I can simply have an instance variable and getter in my domain model that holds the current history log entry I want added. I wrapper this getter with a value adapter. The composite or main presenter can register an interest in changes to this aspect of the model. The main presenter can handle all the communication with the subpresenter including adding the value of the adapter to the ListPresenters model and keeping the list scrolled correctly. I suspect this might be simplified even further but one thing at a time. I haven't tried this yet, but it seems like a very simple approach. Seems like MPD is very malleable. I guess that is the idea<g> On Sat, 20 Jan 2001 00:31:12 -0800, Eric Langjahr <[hidden email]> wrote: >More MVP questions<g>. > |
In reply to this post by Eric Langjahr
I spent some time composing a long reply to this (20K including attachment
in the end). I posted it a couple of hours ago but although it appears on my local news server it doesn't seem to have made it out into the rest of the world. Did anyone not using Claranews see it? I would post it again but it may just have got delayed and multiple postings of a 20K article might generate a few "sniffy" remarks <g> Ian |
Haven't seen it yet, IAN, but I am looking forward to it.
In general I do seem to have things working. Need to look at how I can scroll list without screen flashes. I know there is a windows api to do this but not sure what best way is in Dolphin yet. One behavior I see is the following: When I do the [self model run] fork The UI window does not appear UNTIL the model starts triggering events. I wonder if there is a better way to start the process that will get the UI window up immediately. Some kind of trigger idle event or some way to get the dolphin ui going. When I was running this code in a workspace the Dolphin UI was non responsive. But now when the code is running and logging events in the history, all the other development dolphin windows are responsive. So I guess I need to understand exactly what is going on here a bit better<g>. At any rate, looking forward to reading your long post. I appreciate it. Boy this is fun<g> On Sat, 20 Jan 2001 17:01:34 -0000, "Ian Bartholomew" <[hidden email]> wrote: >I spent some time composing a long reply to this (20K including attachment >in the end). I posted it a couple of hours ago but although it appears on my >local news server it doesn't seem to have made it out into the rest of the >world. > >Did anyone not using Claranews see it? I would post it again but it may >just have got delayed and multiple postings of a 20K article might generate >a few "sniffy" remarks <g> > >Ian > > > > > |
In reply to this post by Ian Bartholomew
"Ian Bartholomew" <[hidden email]> writes:
> Did anyone not using Claranews see it? I would post it again but it > may just have got delayed and multiple postings of a 20K article > might generate a few "sniffy" remarks <g> Didn't see it from my newsserver... Chris. |
In reply to this post by Ian Bartholomew
In article <Pvja6.185352$[hidden email]>, Ian Bartholomew wrote:
> Did anyone not using Claranews see it? I would post it again but it may > just have got delayed and multiple postings of a 20K article might generate > a few "sniffy" remarks <g> > I can't see it from my french ISP, january 20, 21:45 GMT. ---- Dominique Dartois |
In reply to this post by Eric Langjahr
Eric,
> Haven't seen it yet, IAN, but I am looking forward to it. Strange. It's been seen passing through Canada so it did escape from the local news server. Thanks to everyone who reported (non) sightings of it <g> Ill repost it here so apologies to anyone who sees it twice. The only difference between this and the original is that I've zipped the attachment in case it makes a difference - the first attachment was a pac. I hope expectations of what it contains haven't been raised too high by the wait!! -~-~-~-~-~ Eric, > 1. Is the fork approach in the viewOpened method a reasonable one? > Almost all the MPD examples I have seen tend to be examples where the > user causes things to happen. Not where the window opens and some > processing immediately starts. Yes, forking is the way to do this as you want the model to be running at the same time (in effect) as the Shell. However you have to be a bit careful about how you do this as it is very easy to fork off a process that never terminates. In your example you could end up with the Model running after the main Shell had been closed which might not be what you want. There are a number of ways to do this but, IMHO, one of the safest is to put the forked process into the model, where it probably belongs, and control this forked process externally, from the shell. > 2. The normal ListPresenter keeps the selection at the top as items > are added. I want to scroll this list as the items are added so that > the bottom is always in view. Just use normal MVP behaviour - The model maintains a list of all the log events in a simple OrderedCollection. When an addition is made to this list the model triggers and event saying that the list has been changed. The associated presenter has registered for this event and knows when it occurs that it must update it's ListPresenter. AModel>>logEvent: anLogEvent logEvents addLast: aLogEvent. self trigger: #logChanged AModel>>logEvents ^logEvents copy AShellPresenter>>createSchematicWiring aModel when: #logChanged send: #onLogChanged to: self AShellPresenter>>onLogChanged aListPresenter list: aModel logEvents; selection: aCollection last If you want to ensure that the collection of logged events remains a bit more private (i.e. you really don't want to provide an accessor method in the model) then you can pass the logEvents collection as part of the triggered event. AModel>>logEvent: anLogEvent logEvents addLast: aLogEvent. self trigger: #logChanged: with: logEvents copy AShellPresenter>>createSchematicWiring aModel when: #logChanged: send: #onLogChanged: to: self AShellPresenter>>onLogChanged: aCollection aListPresenter list: aCollection; selection: aCollection last You could also do something similar but sharing a single ListModel between the Shell and the Model. This means you wouldn't have to worry about passing the logEvents collections about, just the fact that it had changed. OK. An example of the two concepts above. NB. I don't know how your model class works so the following just checks every 1.5 seconds and add the current time, if contains an odd number of seconds, to the log. Model subclass LogModel with two instVars - process and logEvents LogModel>>start logEvents := OrderedCollection new. process := [[ self checkForUpdates. Processor sleep: 1500] repeat ] forkAt: Processor userBackgroundPriority This is sent by the Shell when it wants the model to start logging events. It forks off a process that checks to see if any new events have occurred, dealing with them if they have, and then goes to sleep for 1.5 seconds. I've used #forkAt: instead of a simple #fork as it tells the processor that if something more important comes up, the UI needs updating for example, then this process can be interrupted and the higher priority process run. It doesn't make a lot of difference in this case but if the #checkForUpdates method was slow then it could make the application seem much more responsive. LogModel>>stop process notNil ifTrue: [ process terminate. process := nil] Sent by the Shell when it wants the process to stop sending log updates. If this is not called, and the Shell tries as hard as it can to make sure it is, then you can end up with the process running after everything interested in the log has disappeared. Even removing all references to the LogModel instance will not help as the fact that the process' block is referencing itself prevents normal garbage collection and finalisation. LogModel>>checkForUpdates | update | update := self getUpdate. update notNil ifTrue: [ logEvents addLast: update. self trigger: #logUpdated: with: logEvents copy] This just checks to see if an update has occurred and, if so, adds the new update and triggers an event passing the new list as a parameter. It passes a copy for two reasons - - Security - it prevents any external changes to a privately owned object - It provides a different object each time (even though the contents of the object are the same). This prevents a peculiarity of the Dolphin ListPresenter where it won't update a list if it thinks the list hasn't changed. LogModel>>getUpdate ^Time now asSeconds odd ifTrue: [Time now] Just a way of generating random updates. This answers the current time if it contains an odd number of seconds or nil (via the implicit ifFalse: []) if even. That's it for the LogModel. This can be tested in a workspace - lm := LogModel new. lm when: #logUpdated: send: #bell to: Sound. lm start You should now start hearing occasional beeps from your system speaker. To show how the process continues, even though we have cleared our only reference to the LogModel, evaluate the following and notice that the beeps keep on beeping. lm := nil As we can't now send "lm stop" (we have set lm to nil) we have to stop the process by using the image to locate the instance of LogModel and stopping it that way LogModel allInstances do: [:each | each stop] The Presenter is quite straightforward. The associated view will contain one ListPresenter.Default view named "view" and the class will have one instance variable named "listPresenter" Shell subclass Log Class method - Log class>>defaultModel ^LogModel new Instance methods - Log>>createComponents super createComponents. listPresenter := self add: ListPresenter new name: 'list' Log>>createSchematicWiring super createSchematicWiring. self model when: #logUpdated: send: #onLogUpdated: to: self As normal. We connect to the ListPresenter MVP in the listPresenter instVar and also tell our model to let us know when the #logUpdated: event occurs. [Diversion] When we triggered the #logUpdated: event in our model we passed an argument (the current log collection) with it. The #when:send:to: statement above implicitly tells Dolphin to pass the argument on, so the collection ends up being passed as the argument to the #onLogUpdated: method. You can also pass multiple arguments (as below) and Dolphin will pass them on for you but the method signatures _must match. If the trigger indicates two arguments (two embedded colons) then the target selector must also have two colons. If not then some arguments will be lost in transition. (x) self trigger: #arg1:arg2: with: 'argOne' with: 'argTwo' -------- (y) x when: #arg1:arg2: send: #onArg1:arg2: to self -------- (y) onArg1: arg1String arg2: arg2String arg1String will equal 'argOne' arg2String witll equal 'argTwo' Log>>onViewOpened super onViewOpened. self beFinalizable. self model start When the Log shell is opened we want the model to be started and this is the easiest place to do it. The #beFinalizable reference is just a piece of insurance here that tells the VM that as a final check, before this instance is destroyed, it should send #finalize to the instance so that it can tidy up. This usually lets the instance destroy any operating system resources that it may have acquired but in this case we will want to make sure the process in the model is stopped. It won't be necessary if the Log shell was closed normally but will only be used, as insurance, if for some reason the Log shell closed abnormally Log>>onViewClosed super onViewClosed. self finalize Log>>finalize self model stop The two ways of ensuring the model is stopped. Normally #onViewClosed is used and #finalize is called that way. If, for some reason, #onViewClosed is nor run then the default finalization that we asked for will call #finalize directly. Log>>onLogUpdated: aCollection listPresenter list: aCollection; selection: aCollection last The model has triggered the event to say it has been updated, and passed the updated collection as well. We end up here and tell the ListPresenter (the ListModel associated with the ListPresenter MVP) the new collection. The scrolling is achieved by selecting the new log entry - by default the last in the list. If we could not guarantee that it would be the last in the list (the list was sorted for example) we could pass the new log entry as part of the triggering, in addition to the complete list, and we could still select it here. That's it. Attached is a D4 package (text so users of 2/3 will still be able to look at it even if they can't load it) containing a working version of the above - which was typed in and may contain the occasional code typo? Ian begin 666 Log.zip M4$L#!!0````(`(NU-"I_*"#&" @``(8=```'````3&]G+G!A8\U87V_;.!)_ M3H%^!R;[X/:P+D19DJ44NX#C)$6!;%MLMKV'(@LH%FUK*XL^2=Y<[GK?_69( M#B71CN.T>\"!D2V2\W]^,Z3SE:W3V9=T(=C7Y\_H]?0G]L&\ENE*G++!E5P, M7K4$Z_2?GT15Y[(\9=[KY\^.;M,ZGTWE:B7*1I.S>BGOD*?E4D1&L&4?:)J3 M29:Q9BFL.?6LRM=-?=+;G!5I72N;ZA]9(64MV$HT2YG1VJ*0MVE!LTK4<E/- MM!<HBH0K.>]P$6Q/L^R4_0`FO^Y.?I&9*'#E'D34HICW7-%:2<).$FW*7A*R M;R?1R5E>IM4]>Z-=>N>X<*MVW[1*3MF+:]&P4MQU)+T$42>_4AA<&6E1_-HU MH2?"1H(-?V:#<S%/-T7#_LS%W>"UHZ&;ODI4XA^;O,Z;[;#7HOG0V4=];S- M3-[<.WH'Y[)8+_-RARHC[%BIG2I @'%YF3> I_I$K:ODL7ISJQ+=22A(R\NZ M2<N9^)16>7I;D.>#=25G`H05<G'Q)QA5#X!8\;N4N+&6LCC/9Z@3=NVRHG_[ M@(8!V':]%(5KV1ZCBKS&D-5@CZC^)P:=7*DR^D4!VH3OY*+,F)S;6LQL?/4^ M#HHHF]FJ'QSW-A:;'#+YXLW'M^=L7LG5=5/EY0+H_AU=7$9\.@F'TXMS?QB, M@LEP<G%Y/CR?3CS_<N+'9Q/^G\%+)>ZX590V8B'1MTM9J<0??RR5?_D\%]DQ M.^Y2ZPI%4B5FMA2S+S#YN,Y #!;;5[91[]CXCLPK-#X$&EN(1A.^:O?R^EV. M^#G*YY=I44-7_(RS(XL7!LB]2FN(Q,;R'ATI>>#Y8B$J2#=0:\D0FKN\69ZV M>(-(KN]O%-.1LME:`6LGUTNY*3*6EO6=J."+R=L_Q*QALF)E7K!\CN5704W# M4TIC`A;?T>^_Y2M<NV-I?2UFLLQJ)J%<\_EOU0;=H/VN;@!,U<"\M0YB\[[* M0$4VE44A%-*P9-%-JAV@^:RBHKQV8J[B\4%3@M5U(<3ZE/'0\VZ@$ZY%VMRP MN:R^3"""+=FF%M49X'!1R4V9?:AR0$!S;VR4ZX[R4C8V0\8SY1#M0P6MH&E2 M8FA9Y?7&04\/:Y VQY7COPTW7>BM-[=%/GM$ADWG-W&KA'PCIUSO8;3EO*N2 M]Q1Q&'+/\\/SH7_IQ<,@N(B'R<5E,IQXR91'L1>=3:;=(CZX?K=*MP)H"+A< MK&6)2(3LU9LU%(&[@7GM-4Q;S^I,N>IM`7+I<H,\@XZJ:\CV*FWRV=]S=-;1 MY^RB4J5CI8)_MQ2E4^:@$$]265YU%AMYJDQ3:J&[ID7^+]$7I?*&VWW.M"V_ M+7\1V+C2HWIM"E+/>EL,HM\8'9_@9)\6<!9DUN'NHO63C.VPO5^+<HM-+UJV M6W&I&?$`>N5ZJIH-I-]B8*L`G5P_4@D/\#NY>ZH0Z_H3^7H)?#ISFX1OX]69 MV-L"-+>Z3#G5E^F+']V>?K=M!@JHHUBQNNJ[O'O5,_>Z2Q<1NIZ:^0N:ZVLC M"*J8N4BA%::>>W?5EPQ5+@#W+][K$Q/;F%9VW<A*G-WC3?3YLQ\^CT8LAK^ M11$;^2R(\7/L,9]QGWF,PX,CCACW0G@XXSQAL6]>88GC]QB>@"6)6O;,@!7. M1A&RDXK]C)8,_KB/VY%:CT)%I2C':@=6@,>P:6DD,6"Q9\AAZGN*GDP*`_A( MX'E4`X\X"Q3/[A#%+#8A(MFMDTZP'C8'I$! 1MU VQ" &5ZLK#'N/>:6";1R M">G0!([VC=C8T$6Q9O?P&U/1(0LB0Y2TIF@"PP"/X]DH1MTH=RM\?F"-ZP^> MT(Y/*_B$H$"]X6I"IK@C@FW4IT+!6]MYR^!CM(PF>AM;78<-<,-G?@^X8PR& M9SSW#&Y"_8UXC-44`6EW-&6B$8Y$>Q+(?=="'K76D'<\-B]_A37;B4BVXG"0 M502N,:G2AF"%D:=6%2=B&C;5V^8H4*L2`\EDN#=27F,`K./<+B.)504C2%0L MS!KH,GB HE8P)U6.Z>.Q@9=GT=^ZC16+\UZE4E&IA^^*_/<GW\8Y,:$@`%"- M>@>:TH;:':C+5SKV`RS6X4XZW3MHB[)%7\=)=[A >&SP<6RR9W$2Z"#X/IJ1 M/#6,462;J[;6Q2\LQG9F6AM%T\@8D49<XMB9$#J=(PDU&&S&!%&$+E%Y[IGI MAQ[K!XU'!J;;%0+G$<@X3*NG8M7&`PU&>>VI-=)MP4NTWSJ3^V#;QM0>'01- M@XW.X1JQ4 -2B:&Z-=C"K2WO_,#-YXB4<WK9IN$^=7W;_FW=^[R5;OA#=V4' M4M0IR*G20@4T6TM<PPA"%26/^\:=;VN:!5 \QN(#L$/C@DJ'::SZ$P01JAMT MC/5GC&DCHP^_U@3$%+3<?9/\,/RN1WFAC"*!'KWQF)3M@7D0FY<=N4U&6V_M M'KW8!-J@FXY$M>P=.!YN7@Y=$.V U<%5O(U*/)X>H-V.R0$UL>M.9 _>IQ4% MG838)EJ4CVTW#7;CG49[W <1ZZ<0&[Q>>1BC-+X7HUL0#:SD4:OC@!QNU;$/ M%U_'+P+F&/4A(^U#G:N&J:/H#MZ&"J\<V(QZO;ISO:>S7A]4C]XW_I]^=="A M;G\OJ)M'TCO"S&]&LHN.:>WSV--3$X*QCD"26%IJW'_%B4?6DF<J?>9F2M<P MNBX1+:=T/_Y[*M!3`@7F71UY;7K<(R9@=*M':7M_7)OKC?(T./1GB77"@A%O MA0\ET].2H(/#7^AK?SSKK9%ZH_\_^E]02P$"% `4````" "+M30J?R@@Q@@( M``"&'0``!P`````````!`" `MH$`````3&]G+G!A8U!+!08``````0`!`#4` (```M" `````` ` end |
In reply to this post by Eric Langjahr
[hidden email] (Eric Langjahr) wrote (abridged):
> I can simply have an instance variable and getter in my domain model > that holds the current history log entry I want added. I'm not sure what is going on, but you may be able to avoid the getter by passing a parameter with the event. Eg: MyModel>>add: anItem container add: anItem self trigger: #addedItem: with: anItem MyPresenter>>createSchematicWiring model when: #addedItem: send: #onAddedItem: to: self MyPresenter>>onAddedItem: anItem "No need to query the model for the item - we already know." "Do whatever is needed to add the item to the View and scroll it into view with minimal flicker." This avoids having to add methods like #itemMostRecentlyAdded to the model. That stuff needs extra mutable state and goes out of date when some other item is added, so you get lots of nasty temporal coupling. Using #trigger:with: can lead to much simpler code. Generally the model should make its events precise, and include available info with them, so that other components don't have to work to recover what it already knows. Dave Harris, Nottingham, UK | "Weave a circle round him thrice, [hidden email] | And close your eyes with holy dread, | For he on honey dew hath fed http://www.bhresearch.co.uk/ | And drunk the milk of Paradise." |
In reply to this post by Ian Bartholomew
Ian
I can't see it either - Pipex news server - UK David "Ian Bartholomew" <[hidden email]> wrote in message news:Pvja6.185352$[hidden email]... > I spent some time composing a long reply to this (20K including attachment > in the end). I posted it a couple of hours ago but although it appears on my > local news server it doesn't seem to have made it out into the rest of the > world. > > Did anyone not using Claranews see it? I would post it again but it may > just have got delayed and multiple postings of a 20K article might generate > a few "sniffy" remarks <g> > > Ian > > > > > > |
OK, I know when I'm beaten!!.
I've sent it twice. I'm told the first was seen in Canada and I _know the second reached Germany as I saw it appear on news.cis.dfn.de a minute or so after I posted it. I have no idea why it is not propagating as normal to the rest of the world. Anyway, I shan't try posting it again but I've made it available on my web site at the following url, should anyone still be interested <g> http://www.iandb.org.uk/~iandb/log.zip Ian |
Free forum by Nabble | Edit this page |