home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / ddjmag / ddj9105.zip / WIDGETS.ASC < prev   
Text File  |  1991-03-15  |  17KB  |  636 lines

  1. _MAKING SMALLTALK WITH WIDGETS_
  2. by Kenneth E. Ayers
  3.  
  4. [LISTING ONE]
  5.  
  6. "The Appointment class definition"
  7.  
  8. Object subclass: #Appointment
  9.   instanceVariableNames: 
  10.     'user date startTime endTime notifyTime text '
  11.   classVariableNames: ''
  12.   poolDictionaries: ''
  13.  
  14. "****  Appointment class methods  ****"
  15. timePrintString:aTime
  16.         "Answer a string with a representation of
  17.          aTime formatted as hh:mm AM/PM"
  18.  
  19.     | amPM hours minutes minStr |
  20.  
  21.     amPM := 'AM'.
  22.     hours := aTime hours.
  23.     minutes := aTime minutes.
  24.     hours = 0
  25.         ifTrue: [hours := 12.  amPM := 'PM']
  26.         ifFalse:[
  27.             hours >= 12
  28.                 ifTrue:[
  29.                     amPM := 'PM'.
  30.                     hours > 12
  31.                         ifTrue:[hours := hours - 12]]].
  32.     minutes < 10
  33.         ifTrue: [minStr := '0', minutes printString]
  34.         ifFalse:[minStr := minutes printString].
  35.     ^hours printString, ':', minStr, ' ', amPM.
  36.  
  37. "****  Appointment instance methods  ****"
  38. < anAppointment
  39.         "Answer true if the receiver's date
  40.          and notify time are earlier than
  41.          those of anAppointment"
  42.  
  43.     ^(date < anAppointment date)
  44.         or:[(date = anAppointment date)
  45.             and:[notifyTime < anAppointment notifyTime]].
  46.  
  47. <= anAppointment
  48.         "Answer true if the receiver's date
  49.          and notify time are the same or
  50.          earlier than those of anAppointment"
  51.  
  52.     ^(self < anAppointment) or:[self = anAppointment].
  53.  
  54. = anAppointment
  55.         "Answer true if the receiver's date
  56.          and notify time are the same as
  57.          those of anAppointment"
  58.  
  59.     ^(date = anAppointment date)
  60.         and:[notifyTime = anAppointment notifyTime].
  61.  
  62. > anAppointment
  63.         "Answer true if the receiver's date
  64.          anf notify time are later than
  65.          those of anAppointment"
  66.  
  67.     ^(self <= anAppointment) not.
  68.  
  69. >= anAppointment
  70.         "Answer true if the receiver's date
  71.          and notify time are the same or
  72.          later than those of anAppointment"
  73.  
  74.     ^(self < anAppointment) not.
  75.  
  76. checkTime:timeNow date:dateToday
  77.         "Answer true if it is time to notify
  78.          the user of his or her appointment"
  79.  
  80.     ^(date < dateToday)
  81.         or:[(date = dateToday)
  82.                 and:[notifyTime < timeNow]].
  83.  
  84. date
  85.         "Answer a Date, the appointment's date"
  86.     ^date.
  87.  
  88. date:aDate
  89.         "Set the appointment's date to aDate"
  90.     date := aDate.
  91. endTime
  92.         "Answer a Time, the appointment's end time"
  93.     ^endTime.
  94. endTime:aTime
  95.         "Set the appointment's end time to aTime"
  96.     endTime := aTime.
  97.  
  98. info
  99.         "Answer a String with the information on
  100.          this appointment"
  101.  
  102.     ^OrderedCollection new
  103.         add:user, ' has an appointment';
  104.         add:'on ', date formPrint,
  105.             ' at ', (self timePrintString:startTime);
  106.         add:String new;
  107.         addAll:text;
  108.         yourself.
  109.  
  110. notifyTime
  111.         "Answer a Time, the time at which the
  112.          user is to be notified"
  113.     ^notifyTime.
  114.  
  115. notifyTime:aTime
  116.         "Set the time at which the user is to
  117.          be notified to aTime"
  118.     notifyTime := aTime.
  119.  
  120. printOn:aStream
  121.         "Add a representation of the receiver
  122.          to aStream"
  123.     aStream
  124.         nextPutAll:date formPrint;
  125.         nextPutAll:' @ ';
  126.         nextPutAll:(self timePrintString:startTime);
  127.         nextPutAll:' - ';
  128.         nextPutAll:(text at:1).
  129.  
  130. startTime
  131.         "Answer a Time, the appointment's start time"
  132.     ^startTime.
  133.  
  134. startTime:aTime
  135.         "Set the appointment's start time to aTime"
  136.  
  137.     startTime := aTime.
  138.  
  139. text
  140.         "Answer an Array containing the lines of text
  141.          that describe the appointment"
  142.     ^text.
  143.  
  144. text:aTextArray
  145.         "Set aTextArray as the lines of text
  146.          that describe the appointment"
  147.     text := aTextArray.
  148.  
  149. timePrintString:aTime
  150.         "Answer a string with a representation of
  151.          aTime formatted as hh:mm AM/PM"
  152.     ^self class timePrintString:aTime.
  153.  
  154. user
  155.         "Answer the user for whom the appointment
  156.          was created"
  157.     ^user.
  158.  
  159. user:userName
  160.         "Set the user for whom the appointment
  161.          was created to userName"
  162.     user := userName.
  163.  
  164.  
  165.  
  166. [LISTING TWO]
  167.  
  168. "The AppointmentBrowser class definition"
  169.  
  170. ApplicationWindow subclass: #AppointmentBrowser
  171.   instanceVariableNames: 
  172.     'user appointments appointmentList
  173.      dateEditor textEditor notifyTimeEditor
  174.      endTimeEditor startTimeEditor '
  175.   classVariableNames: ''
  176.   poolDictionaries: ''
  177.  
  178. "****  AppointmentBrowser class methods  ****"
  179.  
  180. open
  181.         "Prompt for a user name and then open
  182.          an AppointmentBrowser for that user"
  183.     | userName |
  184.  
  185.     Cursor offset:Display boundingBox center.
  186.     userName := PromptDialog
  187.                     prompt:'Enter user name'.
  188.     userName isNil ifTrue:[^nil].
  189.     ^self openOn:userName.
  190.  
  191. openOn:aUserName
  192.         "Open an AppointmentBrowser for aUserName"
  193.  
  194.     ^super new
  195.         openWithInitializeMethod:#initializeUser:
  196.             arguments:(Array with:aUserName).
  197.  
  198. "****  AppointmentBrowser instance methods  ****"
  199.  
  200. addAppointment
  201.         "The user as pushed the 'ADD' button so
  202.          we need to construct a new appointment
  203.          record and add it to the user's list"
  204.     | appointment index |
  205.  
  206.     appointment := Appointment new
  207.             user:user;
  208.             date:dateEditor date;
  209.             startTime:startTimeEditor time;
  210.             endTime:endTimeEditor time;
  211.             notifyTime:notifyTimeEditor time;
  212.             text:textEditor stringList deepCopy;
  213.             yourself.
  214.     appointments add:appointment.
  215.     index := appointments indexOf:appointment.
  216.     self updateAppointmentList.
  217.     appointmentList selectItem:index.
  218.  
  219. changeAppointment
  220.         "The user has pushed the 'CHANGE' button so
  221.          we need to remove the currently selected
  222.          appointment and then add a new on with the
  223.          current values"
  224.  
  225.     appointmentList disableDrawing.
  226.     self removeAppointment.
  227.     appointmentList enableDrawing.
  228.     self addAppointment.
  229.  
  230. createWindow
  231.         "This method was generated by the
  232.          Widgets/V 286 Interface Editor"
  233.  
  234.   ^TitledWindow new
  235.     yourself;
  236.     title: 'Appointment Browser';
  237.     closable: true;
  238.     iconizable: true;
  239.     size: 415 @ 322;
  240.  
  241.     addWidget: (
  242.         appointmentList := ListBox new
  243.             yourself;
  244.             nameForInstVar: 'appointmentList';
  245.             on: #select send: #selectAppointment:;
  246.             title: 'Appointments';
  247.             size: 390 @ 133
  248.  
  249.     ) position: 12 @ 9;
  250.     addWidget: (
  251.         dateEditor := DateWidget new
  252.             yourself;
  253.             nameForInstVar: 'dateEditor';
  254.             title: 'DATE:';
  255.             date: (
  256.                 Date newDay: 16
  257.                      month:  #December
  258.                      year:   1990
  259.             );
  260.             size: 114 @ 18
  261.     ) position: 47 @ 156;
  262.     addWidget: (
  263.         TitledPane new
  264.             yourself;
  265.             title: 'WHAT FOR?';
  266.             size: 233 @ 116;
  267.  
  268.             addWidget: (
  269.                 textEditor := TextEditWidget new
  270.                     yourself;
  271.                     nameForInstVar: 'textEditor';
  272.                     verticalScrollBar: true;
  273.                     horizontalScrollBar: false;
  274.                     size: 219 @ 90
  275.             ) framer: (
  276.               FramingParameters new
  277.                 xCentered;
  278.                 yCentered
  279.             )
  280.     ) position: 170 @ 151;
  281.  
  282.     addWidget: (
  283.         notifyTimeEditor := TimeWidget new
  284.             yourself;
  285.             nameForInstVar: 'notifyTimeEditor';
  286.             title: 'NOTIFY AT:';
  287.             time: (
  288.                 Time new seconds: 74673
  289.             );
  290.             size: 144 @ 18
  291.     ) position: 16 @ 269;
  292.     addWidget: (
  293.         TitledButton new
  294.             yourself;
  295.             on: #release send: #changeAppointment;
  296.             title: 'CHANGE';
  297.             size: 77 @ 19
  298.     ) position: 326 @ 269;
  299.     addWidget: (
  300.         endTimeEditor := TimeWidget new
  301.             yourself;
  302.             nameForInstVar: 'endTimeEditor';
  303.             title: 'END TIME:';
  304.             time: (
  305.                 Time new seconds: 74673
  306.             );
  307.             size: 136 @ 18
  308.     ) position: 24 @ 230;
  309.     addWidget: (
  310.         startTimeEditor := TimeWidget new
  311.             yourself;
  312.             nameForInstVar: 'startTimeEditor';
  313.             on: #valueChanged send: #startTimeChanged:;
  314.             title: 'START TIME:';
  315.             time: (
  316.                 Time new seconds: 74613
  317.             );
  318.             size: 148 @ 18
  319.     ) position: 12 @ 192;
  320.     addWidget: (
  321.         TitledButton new
  322.             yourself;
  323.             on: #release send: #addAppointment;
  324.             title: 'ADD';
  325.             size: 77 @ 19
  326.     ) position: 170 @ 269;
  327.     addWidget: (
  328.         TitledButton new
  329.             yourself;
  330.             on: #release send: #removeAppointment;
  331.             title: 'REMOVE';
  332.             size: 77 @ 19
  333.     ) position: 248 @ 269.
  334. initializeUser:argArray
  335.         "Initialize an AppointmentBrowser for the
  336.          user whose name is given in argArray"
  337.  
  338.     user := argArray.
  339.     (Appointments includesKey:user)
  340.         ifFalse:[
  341.             Appointments
  342.                 at:user
  343.                 put:(SortedCollection
  344.                         sortBlock:[:a :b| a < b])].
  345.     appointments := Appointments at:user.
  346.     self window
  347.         on:#activated send:#updateAppointmentList;
  348.         title:'Appointments for ', user.
  349.     dateEditor date:Date today.
  350.     startTimeEditor time:(Time fromSeconds:32400).  "9:00 AM"
  351.     self
  352.         startTimeChanged:startTimeEditor time;
  353.         updateAppointmentList.
  354.  
  355. removeAppointment
  356.         "The user has pushed the 'REMOVE' button so
  357.          we need to remove the selected appointment
  358.          from the user's list"
  359.     | index appointment |
  360.  
  361.     appointments size = 0 ifTrue:[^self].
  362.     index := appointmentList selectionIndex.
  363.     appointment := appointments at:index.
  364.     appointments remove:appointment ifAbsent:[].
  365.     self updateAppointmentList.
  366.     index > appointments size
  367.         ifTrue:[index := appointments size].
  368.     appointmentList selectItem:index.
  369.  
  370. selectAppointment:aString
  371.         "The user has selected an appointment so
  372.          we need to fill-in all of the appropriate
  373.          field editors"
  374.     | appointment |
  375.  
  376.     appointment :=
  377.         appointments
  378.             at:appointmentList selectionIndex.
  379.     dateEditor date:appointment date.
  380.     startTimeEditor time:appointment startTime.
  381.     endTimeEditor time:appointment endTime.
  382.     notifyTimeEditor time:appointment notifyTime.
  383.     textEditor stringList:appointment text.
  384.  
  385. startTimeChanged:aString
  386.         "The user has changed the start time
  387.          so we need to supply rerasonable
  388.          defaults for the end time and the
  389.          notify time"
  390.     | aTime |
  391.  
  392.     aTime := startTimeEditor time.
  393.     "Assume appointment is one hour long"
  394.     
  395.     endTimeEditor
  396.         time:(aTime addTime:(Time fromSeconds:3600)).
  397.  
  398.     "Assume notify 5-minutes before"
  399.     notifyTimeEditor
  400.         time:(aTime subtractTime:(Time fromSeconds:300)).
  401.  
  402. updateAppointmentList
  403.         "Update the list of appointments"
  404.     | size list |
  405.  
  406.     (size := appointments size) = 0 ifTrue:[^self].
  407.     list := Array new:size.
  408.     1 to:size do:[:index|
  409.         list
  410.             at:index
  411.             put:(appointments at:index) printString].
  412.     appointmentList  stringList:list.
  413.  
  414. user
  415.         "Answer the name of the user for which
  416.          this browser was opened"
  417.  
  418.     ^user.
  419.  
  420. windowLocation
  421.         "Make the window centered on the screen"
  422.  
  423.     ^#center.
  424.  
  425.  
  426.  
  427. [LISTING THREE]
  428.  
  429. "The AppointmentNotifier class definition"
  430.  
  431. ApplicationWindow subclass: #AppointmentNotifier
  432.   instanceVariableNames: 
  433.     'active running minute text appointment '
  434.   classVariableNames: ''
  435.   poolDictionaries: ''
  436.  
  437. "****  AppointmentNotifier instance methods  ****"
  438.  
  439. acknowledge
  440.         "The user has pushed th 'OK' button, so we
  441.          need to remove the current appointment
  442.          from the user's list"
  443.     | user list |
  444.  
  445.     appointment isNil ifTrue:[^self].
  446.     user := appointment user.
  447.     (list := Appointments
  448.                 at:user
  449.                 ifAbsent:[nil]) isNil
  450.         ifFalse:[
  451.             list
  452.                 remove:appointment
  453.                 ifAbsent:[]].
  454.     text
  455.         stringList:#();
  456.         display.
  457.     appointment := nil.
  458.     minute := nil.
  459.  
  460. activateFor:anAppointment
  461.         "Display the details of anAppointment and
  462.          bring this window to the top"
  463.  
  464.     appointment isNil
  465.         ifTrue:[
  466.             "Previous appointment
  467.              has been dismissed"
  468.             text
  469.                 disableDrawing;
  470.                 stringList:anAppointment info;
  471.                 enableDrawing.
  472.             appointment := anAppointment].
  473.     Terminal bell;  bell.
  474.     Cursor offset:window origin.
  475.     ScreenManager activateWindow:self window.
  476.  
  477. clockEvent
  478.         "A clock tick has been received so we have
  479.          to determine if the minute has rolled over
  480.          and, if so, are any appointments ready"
  481.     | now thisMinute today list appointment |
  482.  
  483.     now := Time now.
  484.     (thisMinute := now minutes) = minute
  485.         ifTrue:[^self].
  486.     minute := thisMinute.
  487.     today := Date today.
  488.     Appointments associationsDo:[:anEntry|
  489.         (list := anEntry value) size > 0
  490.             ifTrue:[
  491.                 ((appointment := list first)
  492.                         checkTime:now date:today)
  493.                     ifTrue:[self activateFor:appointment]]].
  494. closeWindow
  495.         "Before closing this window, terminate the
  496.          process that's monitoring clock events"
  497.  
  498.     active := false.
  499.     "Wait for the process to terminate"
  500.     [running] whileTrue:[Processor yield].
  501.     super closeWindow.
  502.  
  503. createWindow
  504.         "This method was generated by the
  505.          Widgets/V 286 Interface Editor"
  506.  
  507.   ^TitledWindow new
  508.     yourself;
  509.     title: 'Appointment Notifier';
  510.     closable: true;
  511.     size: 183 @ 155;
  512.  
  513.     addWidget: (
  514.         text := TextEditWidget new
  515.             yourself;
  516.             nameForInstVar: 'text';
  517.             verticalScrollBar: true;
  518.             horizontalScrollBar: false;
  519.             size: 169 @ 91
  520.     ) position: 5 @ 6;
  521.     addWidget: (
  522.         TitledButton new
  523.             yourself;
  524.             on: #release send: #acknowledge;
  525.             title: 'OK';
  526.             default: true;
  527.             size: 57 @ 23
  528.     ) framer: (
  529.       FramingParameters new
  530.         xCentered;
  531.         originY: 103 relativeTo: #origin
  532.     ).
  533.  
  534. initialize
  535.         "Initialize a new AppointmentNotifier"
  536.     active := false.
  537.     running := false.
  538.     minute := Time now minutes.
  539.     [self run] forkAt:Processor highUserPriority.
  540. run
  541.         "Run the process that handles clock events"
  542.     | timerSemaphore |
  543.  
  544.     (timerSemaphore := Smalltalk
  545.                         at:#TimerSemaphore
  546.                         ifAbsent:[nil])
  547.             isNil
  548.         ifTrue:[
  549.             timerSemaphore := Semaphore new.
  550.             Smalltalk
  551.                 at:#TimerSemaphore
  552.                 put:timerSemaphore].
  553.     active := true.
  554.     running := true.
  555.     [active]
  556.         whileTrue:[
  557.             timerSemaphore wait.
  558.             self clockEvent].
  559.     Smalltalk removeKey:#TimerSemaphore.
  560.     running := false.
  561.  
  562. windowLocation
  563.         "Make the window centered on the screen"
  564.  
  565.     ^#center.
  566.  
  567.  
  568.  
  569.  
  570. [LISTING FOUR]
  571.  
  572. "Modifications to the Process class methods"
  573.  
  574. timerInterrupt
  575.         "Implement the timer interrupt."
  576.     | timerSemaphore |
  577.  
  578.     PendingEvents add: (Message new
  579.         selector: #clockEvent:;
  580.         arguments: (Array with: 1)).
  581.     KeyboardSemaphore signal.
  582.  
  583.     "**********************************************
  584.     Added by Ken Ayers to support the
  585.     Appointment Manager Application"
  586.  
  587.     timerSemaphore := Smalltalk at:#TimerSemaphore
  588.                             ifAbsent:[nil].
  589.     timerSemaphore notNil
  590.         ifTrue:[timerSemaphore signal].
  591.     "**********************************************"
  592.     self enableInterrupts: true.
  593.  
  594.  
  595.  
  596.  
  597. [LISTING FIVE]
  598.  
  599. "Corrections to TimeWidget methods"
  600. "****  TimeWidget instance methods  ****"
  601.  
  602. time
  603.     | hours |
  604.  
  605.     hours := self hours.
  606.  
  607.     (self meridianEditor value = 'PM')
  608.         ifTrue:[
  609.             hours < 12 ifTrue:[hours := hours + 12]]
  610.         ifFalse:[
  611.             hours = 12 ifTrue:[ hours := 0]].
  612.  
  613.     ^(Time fromSeconds:0)
  614.         hours:hours;
  615.         minutes:self minutes.
  616.  
  617. time: newTime
  618.     | hours |
  619.  
  620.     self time = newTime ifTrue: [^self].
  621.  
  622.     hours := newTime hours.
  623.  
  624.     (hours >= 12)
  625.         ifTrue: [
  626.             self meridianEditor value: 'PM'.
  627.             hours > 12 ifTrue:[hours := hours - 12]]
  628.         ifFalse: [
  629.             self meridianEditor value: 'AM'.
  630.             hours = 0 ifTrue:[hours := 12]].
  631.  
  632.     self hourEditor value: hours.
  633.  
  634.     self minuteEditor value: newTime minutes.
  635.  
  636.