MVP Night School

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
22 messages Options
12
Reply | Threaded
Open this post in threaded view
|

MVP Night School

Andy Bower
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


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Phil Lewis-2
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
>
http://www.object-arts.com/Lib/EducationCentre4/htm/creatingguiapplications. 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
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

mchean-2
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/


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Costas Menico-2
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


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Andy Bower
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


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Andy Bower
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


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Ian Bartholomew-3
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??


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Eric Langjahr
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?
>


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Ian Bartholomew
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


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Frank Sergeant
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!


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Eric Langjahr
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?
>>


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Eric Langjahr
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>.
>


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Ian Bartholomew
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


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Eric Langjahr
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
>
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Chris Double-2
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.


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Dominique Dartois-2
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


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Ian Bartholomew
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


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Dave Harris-3
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."


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

David Royal
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
>
>
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: MVP Night School

Ian Bartholomew
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


12