'From Squeak3.5 of ''11 April 2003'' [latest update: #5180] on 27 April 2003 at 6:13:05 pm'! "Change Set: IRCEnhancements-sbw Date: 28 February 2002 Author: Stephan B. Wessels SM Name: Enhanced IRC Client for Squeak 3.4 GENERAL NOTES: The existing IRC cllient has been left alone and a subclassed client was created. For background IRC protocol information please see http://www.irchelp.org/irchelp/rfc/. See (IRCConnectionEnhanced releaseNotes) for a workspace window containing all release notes. Key new features include: A new user interface having one console window per connection and then having all console messages, channel messages and private messages contained inside the main console window using tabbed swapped panes. Support was added for /WHOIS and /ACTION from the channel chat windows. Other windows do not have complete support for these commands yet. The channel messages text, private messages text, and console messages text panes are read-only. This allows you to put the cursor over either the input pane at the bottom or the actual text pane and begin typing. The input text you type is sent only to the input pane at the bottom. Copy and Find operations are still possible with the read-only text pane content. You can jump to a channel from the channels listing pane by one ot two techniques. First you can select a channel from the produced list. Second, you can use the Prompted Channel button and be asked for the name of a channel you want to join. The channel name does not need to appear in the list. Further a check is made against the channel name you enter and a # is prefixed if you left it off. The console Connect button indicates the state of the connection. It highlights in a color to indicate that you are connected. The label of the button also changes to Disconnect once you are connected. If your connection drops by itself, for some reason, the button color shows another color to indicate it was dropped and the button text is changed to Connect. The Channels list pane is a multi-column pane. The Refresh button also changes color to indicate that a refresh is active. A channel refresh action can take a while so the tag of the Refresh button also attempts to update with an indication of the number of channels fetched so far as an indicator of progress. The channel chat input pane performs Nick Completion. Anywhere on the input text line you are typing you can begin to type a few characters from the beginning of a channel Nick and hit the tab key. This will complete the nick tag for you. If there is more than one Nick that begin with the same characters, the tab key will rotate through them for you. You can also do Nick Completion multiple times on the same line of text. A leading tab on a channel chat input line is invalid and ignored. Also empty input lines are ignored. Highlilght your transmitted messages in the channel chat pane with a color. Also, messages that have your nick in them on the channel chat pane will be highlighted in another color. /ACTION is supported from the channel chat input. You can type in a command like /action looks around room and that message will be sent as an ACTION message to the channel. All ACTION messages are highlighed in the channel chat text pane in italics. The channell chat pane is formatted into 2 columns. The left most column is the nick and the next column to the right shows the typed text. New server configuraton dialogs. Includes the ability to tailor options while connected. Modification of connection parameters for the current session is prohibited while connected. Connection Profiles are supported. You can specify a unique set of connection parameters for any session and save them in a list. The options you select when you create a new profile are those of the current profile. Added a built-in table of many IRC servers you can choose from with a dialog window. You mayhave multiple console sessions open at the same time, each having a unique IRC server connection. The title of the console window reflects the basic parameters of that connected session. Added support for roll back/forward of previously typed text. Use the up and down arrow keys to scroll through text lines. /WHOIS command sends response from the server to your channel chat window now. Only sends private message to the Provate pane that are NOT already being sent to another private messages pane. That is, if you have a private messages pane open the responses there are no longer duplicated in the Private Messages window. Tabbed channel text and private chat panes have their tabs highlighted if messages appear there while you have another pane in front. You can use this feature to watch for activity on a channel or private chat. Channel member JOINS and PARTS are now logged to the channel chat text. There are options to disable this. An option is provided that supported INVITE requests. When you get an INVITE request to join another channel this option will allow automatic opening of that channel then adding you to the members there. An option is provided that automatically opens up a private chat window if you receive an unsolicitied private message. This can include server messages and NICKSERV messages. Added support for multiple line input to a channel and private chat pane. This allows you to know PASTE into a channel. Channel members are now listed in a logical order. Channel operators are at the top, followed by +voiced channel members and then normal members. Also, the nicks for operators are shown in red on the channel members list and the voiced members are show in another color. Added support for the /CLEAR command to console, private chat and channel chat windows. /CLEAR will clear the contents of the text pane. This command is not sent to the IRC server of course. Added /INSPECT to console, private and channel chat input pane commands. This will cause a Squeak Inspector to open up on the model for that text pane. Support provided for NICK change messages receieved from the IRC server. /NICK command is now supported from the channel chat input pane. Date Update ------------- ----------------------------------------------------------------------- 20-apr-2003 No changes to code base. Modified the revisions history format in the change set preamble. 11-Jan-2003 #2 Added logging of channel traffic option. Fixed bug with collision of world open menu. - not needed with 3.4. Defined IRC.FREENODE.NET server. 11-Jan-2003 Added better control over management of topic pane. 21-Nov-2002 #2 Made work with Squeak 3.4. This mostly means that the open menu is properly integrated. 21-Nov-2002 #1 Added basic mIRC color support. Foreground colors are handled for incomming messages. 16-Nov-2002 Button sizes were out of whack for the 'add a profile' dialog. Also made the channel window input pane a little taller to assist with situations where you type two lines of text. 11-Nov-2002 Incorporate previous fixes from the SqueakEnd change set. These include: Fixed bug where creation of a channel did not check for a missing leading # character. Join command already makes this check. Sending private messages would echo what was typed to all the private discussion windows. 25-Apr-2002 Fixed problem where special commands (ACTION, DCC, and SED) were being forced to lowercase before transmit. This was incorrect. Note that the IRC user can type either /ACTION or /action and they will work the same. Also incorporated two bug fixes from Bijan. 4-Apr-2002 Took some advice from Ned Konz and made the small input panes to not have scrollbars. 30-mar-2002 Fixed a problem with the the multicolumn lists logic that impacted the email program Celeste. The problem does not exist in Celeste unless this program is also installed. This update fixes both. 11-mar-2002 #3 Cosmetic stuff. 11-mar-2002 #2 Moved TabbedSwappingControl to Morphic-Palettes system category. Renamed the change set. 11-mar-2002 #1 Channel chat user list menu is now context sensitive. Generalized communications paths software. Now have support for /ACTION command issued from a private message pane. There is a remaining issue with SERVER messages getting lost. 10-mar-2002 #2 Fixed a race condition when processing private IRC messages. The symptom was double lines of text appearing when a new window was first opened if created by an external private message send. The size of the looping collection was changing while it was being processed. More cosmetic tweaks. 10-mar-2002 #1 Code factoring around messages sends. This work is not complete but represents a first pass at new code paths. Fixed problem where channel users list was not getting updated if the case of the selected channel did not match the actual channel name EXACTLY. In IRC channel names are case in-sensitive. Some interesting cosmetic touches. Invite didn't update all panes the first time the channel window opened. Support of rcvd ACTION messages in private message windows. Note that /ACTION commands are not formatted properly in the display. Received ones are shown correctly. Private chat page had overlap on text and input panes. Added support for WHOIS in private chat window. Private message chat that opens up a new window shows the first line of text twice. 7-mar-2002 Added /EXPLORE as valid command. Mode change detection was buggy. Consequently there were problems identifying all operators and members having voice in a channel. Ordering of channel members is now case insensitive. 6-mar-2002 #1 Bug in editing a profile name was fixed. Added logging of commands in the console. Incorrectly setting our nick any time someone changes theirs. Oops. 5 mar 2002 #4 Added button to profiles dialog that saves the current configuration as a new profile. 5 mar 2002 #3 Server based messages that are posted to channel chat panes are now aligned with the columns of other member base chat traffic. 5 mar 2002 #2 More tweaks to the multi column headered morph so that headers show up even when the list is empty. Mode changes noted in channel chat now show NICK of person that initiated the mode change (+/- voice/op). 5 mar 2002 #1 Fixed a bug where profile names could be mismatched from the keys used to store them. Factored the release notes into 3 editable strings. One for features enumeration, one for revisions history and a to-do list. Added some utility functions that make it easier to produce a new change set preamble that agrees with the release notes. Fixed a bug in the multicolumn header morph where it would try to draw headers before the list contents where known. Channels pane redesign of layout. 4 mar 2002 Added provision to edit an existing profile. Also added provision for user to change the server selected from the profile dialog. -41 (published 4 mar 2002) Fixed a bug pointed out by Bijan Parsia. Thanks. -40 Problem with post load initializer. -39 Trim blanks around any added profile name. Generate better initial profile names from server names. Cosmetics. -38 Extended multi column list morph with a new morph PluggableMultiColumnListMorphByItemWithHeader used in the profiles selection dialog. This morph provides multi-column ability with a built-in header row. Disable active editing of configuration while connected. Profiles are working. Note that the current options are copied into any new profile you create. -37 Factoring of message sending code. Still needs more cleanup in that code. Made options available on a per connection basis. Each connection can have their own unique options. When you open up a new console window from the World menu the options are set to the defaults. When you open up a new console from the New Console button the options from the existing console window are copied forward to the new one. Added support for Profiles. A Profile contains a nick, server, and options which can be used for initializing a connection. Profiles management for the user are not coded yet. -36 Factoring. -35 Handles nick change messages received from IRC server. Also properly handles /whois and /nick changes initiated from channel window. -34 Fetch of channels list shows update count while fetching. Detect when IRC server fails to send channel list because of server loading. -33 fixed problem parsing PART messages from some servers Generalized the reserved keywords (CLEAR, INSPECT) to be included in any IRC input pane (console, channel or private messages). -32 Support /CLEAR in channel chat window. Added support for /INSPECT too. -31 Fixed some bugs with mode changes in channels Fixed problem with connection dialog/setup window after being collapsed. Better factoring of TabbedSwappingControl morph. Lots of factoring in IRCConnectionEnhanced. -30 Fixed bug in servers populate code. -29 Show channel member names in logical order with color highlighting for operators and voiced members. Detected mode changes that impact member status. Fixed bug where PART messages were being sometimes sent to the wrong channel pane. -28 Fixed problem with post load initializer where populated servers database was empty. -27 Bug fix in paste/copy code for channel text. -26 Handle multiple line inputs, such as can come from a paste operation. -25 Cosmetics in text messages from server. Added close option to channel text menu. Command keys work from the channel and console text panes now. -24 Change set maintenance. -23 Fixed problem where new private messages were not causing the tab to update. Open up a chat window pane for private messages we receive except for from server. Member channel joins, parts and quits are now shown in the channel window panes. Support INVITE requests. Added Options pane to Setup dialog. Factored out most of the swap/tabbed pane into a new pane design used in the Config dialog. -22 minor code factoring. -21 /ACTION messages supported in channel chat. -20 Added support for /WHOIS command to channel chat windows and users menu. -19 Added rollback of messages to console window pane. Fixed bug in private chat window where ""me"" was still being used as nick for outgoing messages. Fixed places where we were using asLowercase instead of asIRCLowercase. Stop sending private messages to private messages redundantly pane if there is already a private chat window open to get them. Factored sound messages for possible future enhancement. -18 Since we can now have multiple consoles open on different IRC servers, the title of the window now reflects the connection. Rollback of previously typed text. Fixed the bug where there was a delay before channel members were displayed. -17 Redesign of the ""population/status"" pane. First hooks put into place to add support for find/copy etc from keyboard in the channel text pane. Keys are not mapped yet. -16 New server configuration dialog allows choices from many possible IRC servers. Added a built intable of several known IRC servers. -15 Dropped the ""me"" designations for the real nick. Factored all the colors into IRCClientColors class. It's not as nice as a having color preferences management, but you can edit them all in one place. An extension would be to allow storing and loading of customized colors without code changes. Highlight transmitted messages in a color, and highgllight recived messages that contain our nick in a color. Highlight ACTION message responses with italic. Channel chat pane breaks nicks into column before messages. Fix problems with automatic nick completion. It used to take more than 1 TAB key hit to get started, and you could not embed TABs in the text line if they were not part of nick search. Also, you could not begin a line with a tab. In the case where someone types a tab in the first column and there are no characters to match against a nick, treat it like someone is typing indented text. -14 Nick completion. We trap the TAB key and rotate through nicks. -13 bug fixes and more cosmetic tweaks -12 bug in menu for channel chat pane fixed. added menu to private chat panes. cosmetics to the population counter pane. -11 Removed connect button from setup dialog. Ensured that setup window could not be closed if edits were not accepted. Indicate if the connection was dropped by color change in connect button. Reconnect channels and private chats that were open if the user connectd after a dropped connection. -10 added some edit menu functions to the readonly chat pane. added menu to user list in chat pane. selected channels button will bring existing channels window into focus if already defined. on manual disconnect, close the closeable tabs to avoid later conflicts. made the users status pane (population: x) read only. cleanup the borders and pane background colors for the user list in the chat pane. cleanup the borders and pane background colors for the channel buttons. private message windows have the same focus properties as the channel browsers. Input goes to the input pane. made console pane readonly and again used the input pane to catch focused keystrokes. replaced console menu with one more appropriate since functions from old menu are now buttons. show channel list refresh is busy by way of button colors. removed special button from channels pane. -9 Better connect/disconnect detect and indication. Release the channel listeners when we close a tabbed pane. Handle rotate left/right of tab panes. -8 Opening up a private chat pane now becomes the active tabbed pane. Highlight the tabbed panes that see messages while you have another pane in front. Check for missing # in front of prompted channel names. -7 detect socket drop and change connect button state to indicate what happened. Fixed missing when someone parted from a channel. -6 bugs fixed -5 new provision for closing tabbed panes. Only specific panes can be closed. Also added buttons to move pane tabs left and right but nothing implemented to support those features yet, -4 more clean up of little bugs -3 clean up of change set contents. -2 fixed problem with send of private messages. Input pane was not constructed properly -1 general release NEED TO DO: (or would be nice to do) These are in no special priority order. - the profile options should NOT be the key names. - cleanup code to add class methods defining new constants for commands I have added. - mIRC colored text - logging - DCC send/recv - PING response for server timeout? Verify this mechanism is working right. I occasionally get disconnects and think this may be why. - add 401 no such nick/channel to channel feedback - /away needs to be supported - disconnect does not close private message panes " ! IRCChannelUsersBrowser subclass: #IRCChannelEnhancedUsersBrowser instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! IRCChannelInfo subclass: #IRCChannelInfoEnhanced instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! !IRCChannelInfoEnhanced commentStamp: '' prior: 0! We will use instances of IRCChannelMember to hold information about and organize our channel members.! IRCChannelListBrowser subclass: #IRCChannelListBrowserEnhanced instanceVariableNames: 'multiColumnMorph refreshButton ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! Model subclass: #IRCChannelMember instanceVariableNames: 'channel nick voice operator topic ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! IRCChannelObserver subclass: #IRCChannelObserverEnhanced instanceVariableNames: 'selectedUser attemptedNicks inPane widestNick nickMatchWord status messagePlayback logFile ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! Object subclass: #IRCClientColors instanceVariableNames: '' classVariableNames: 'ColorTrapCharacter TextColorMap ' poolDictionaries: '' category: 'Network-IRC Chat'! Object subclass: #IRCClientSounds instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! Model subclass: #IRCConnectionDialog instanceVariableNames: 'connection profilesIndex configurationPane denialPane connectedState ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! IRCConnection subclass: #IRCConnectionEnhanced instanceVariableNames: 'portList group msgObserver channelsBrowser channelObservers connectDialog btns motdPane dropped consoleMessagePlayback swapper options ' classVariableNames: 'AllOptionKeys LocalMessageHandlers OutgoingMessageHandlers Profiles SavedOptions ServersList ' poolDictionaries: '' category: 'Network-IRC Chat'! !IRCConnectionEnhanced commentStamp: 'sbw 11/21/2002 19:26' prior: 0! holds a Set of pane names that are being swapped. We recv text from any one of three kinds of panes. #sendChannelString:from:channelName: (channel chat) #sendConsoleString:from: (our own console text) #sendPrivateString:from:to: (a private chat) After processing through a bunch of decisions concerning if the text represents a local commmand (not for IRC consumption) or a special command (requiring unique reformatting of the output message package) we eventually let the calling pane we sent the text through #textWasSent:. The caller can then update the display or whatever. NOTE: If you make changes to the enhanced IRC client, edit the revisionsString class method and then perform a IRCConnectionEnhanced installNewPreambleForIRCChangeSet to install the edit history into the change set preamble.! ]style[(663 15 34 56 57)f1,f1b,f1,f1c136034000,f1! Object subclass: #IRCConnectionProfile instanceVariableNames: 'name nick server options fullName userName ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! IRCDirectMessagesObserver subclass: #IRCEnhancedDirectMessagesObserver instanceVariableNames: 'messagePlayback ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! Object subclass: #IRCMessagePlayback instanceVariableNames: 'playBack index ' classVariableNames: 'RollbackLimit ' poolDictionaries: '' category: 'Network-IRC Chat'! Model subclass: #IRCProfileDialog instanceVariableNames: 'profile parentDialog canceled referenceConnection editing ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! !IRCProfileDialog commentStamp: '' prior: 0! We are invoked because the user wants to: 1) add a new profile 2) edit an existing profile When you want an add, send #openForAddWithOptions: to us. When you want to edit, send #openDialogOn: to us. In both cases we assume that you have hidden your window just before calling us. Do not call our #openDialog method, it is private. ! Object subclass: #IRCProtocolMessage instanceVariableNames: 'prefix command arguments isSpecial isLocal ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! Object subclass: #IRCServer instanceVariableNames: 'serverName address ports group port ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! Model subclass: #IRCServerDialog instanceVariableNames: 'selectedGroupIndex selectedServerIndex selectedPortIndex serversDict parentDialog ' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! !IRCServerDialog commentStamp: '' prior: 0! Use #openDialog to open an instance of ourselves for selection from the user. The user will either 1) close the window (treated like a cancel) 2) press cancel 3) press okay If window closed via close button or user selected cancel, and if there is a parentDialog defined, we send the #noServerSelected to that parentDialog. If the window closed by the okay button and if there is a parent dialog defined, we send #useValuesFromServer:port: to that parent dialog. In all cases our window is closed when we leave here.! IRCMorph subclass: #IRCEnhancedMorph instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Network-IRC Chat'! PluggableListMorph subclass: #PluggableListMorphSpecialHighlighting instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Windows'! PluggableMultiColumnListMorphByItem subclass: #PluggableMultiColumnListMorphByItemWithHeader instanceVariableNames: 'columnOffsets headerStrings headerHeight headerBackColor ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Windows'! PluggableTextMorph subclass: #PluggableInterceptingTextMorph instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Windows'! PluggableTextMorph subclass: #PluggableTextMorphHybrid instanceVariableNames: 'keysInputPane ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Windows'! AlignmentMorph subclass: #TabbedSwappingControl instanceVariableNames: 'frontMost tabs tabQuadrant tabFeedbackColor paneFocusedColor defaultPaneName showMover ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Palettes'! !TabbedSwappingControl commentStamp: '' prior: 0! Pluggable alignment pane that supports child morph panes swapped via tab bar. The "public-API" category show all methods available to the consumer of this morph. ! TextMorphForEditView subclass: #TextMorphForEditViewHybrid instanceVariableNames: 'keysRecvr ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Text Support'! TextMorphForEditView subclass: #TextMorphForInterceptingEditView instanceVariableNames: 'intercepts interceptActive interceptor ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Text Support'! UpdatingThreePhaseButtonMorph subclass: #UpdatingThreePhaseButtonMorphEnhanced instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Widgets'! !UpdatingThreePhaseButtonMorphEnhanced commentStamp: '' prior: 0! allows for the #getSelector to use the get argument value if needed.! !Object methodsFor: 'testing' stamp: 'sbw 3/1/2002 11:44'! isStringMorph ^false! ! !IRCChannelEnhancedUsersBrowser methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 16:34'! talkTo "open a window for talking to the selected user" | user | user _ self selectedUser. user ifNil: [^ self]. ^ IRCEnhancedDirectMessagesObserver openForConnection: channel connection talkingTo: user! ! !IRCChannelInfo methodsFor: 'access' stamp: 'sbw 1/11/2003 08:41'! topic: aString topic _ aString. self changed: #topic! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 11:18'! addMember: memberName "note that memberName is on the channel. memberName should be given in the user's preferred capitalization" | newMember | newMember _ IRCChannelMember fromString: memberName. newMember channel: self name. (members includes: newMember) ifFalse: [ members add: newMember. self changed: #memberNames]! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 11:19'! addMembers: listOfIRCChannelMembers listOfIRCChannelMembers do: [:each | (members includes: each) ifFalse: [members add: each]]. self changed: #memberNames. connection channelMembersLoaded: self! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 12:41'! findMember: memberName | index | index _ members findFirst: [:each | each nick = memberName]. index = 0 ifTrue: [^ nil]. ^members at: index. ! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 14:54'! hasMember: memberName ^(self findMember: memberName) isNil not! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 10:01'! initializeName: aString connection: aConnection name _ aString. members _ SortedCollection sortBlock: [:a :b | a < b]. subscribers _ IdentitySet new. connection _ aConnection! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 10:05'! memberNames "names of the clients subscribing to this channel" ^ members collect: [:each | each nick]! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/7/2002 00:38'! modeChanged: mode forMemberNamed: aString | member | member _ self findMember: aString. member isNil ifTrue: [^ nil]. member modeChanged: mode. members reSort! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 15:11'! nickFor: oldName changedTo: newName | member | member _ self findMember: oldName. member isNil ifTrue: [^ nil]. member nick: newName! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 16:34'! openUserList "open a listing with all the users on this channel" ^ IRCChannelEnhancedUsersBrowser openOnChannel: self! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 10:28'! operatorIndexes "Answers an Interval of indexes for members that are operators." | start stop member | start _ 0. stop _ 0. 1 to: members size do: [:index | member _ members at: index. member isOperator ifTrue: [ start _ 1. stop _ index] ifFalse: [^Interval from: start to: stop] ]. ^ Interval from: start to: stop! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 12:42'! removeMember: memberName "note that memberName has left (PART-ed or QUIT-ed) the channel" | member | member _ self findMember: memberName. member isNil ifTrue: [^nil]. members remove: member ifAbsent: [^ self]. self changed: #memberNames! ! !IRCChannelInfoEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 13:21'! voicedIndexes "Answers an Interval of indexes for members that are voiced." | start stop member | start _ members findFirst: [:x | x hasVoice]. stop _ 0. start = 0 ifFalse: [start to: members size do: [:index | member _ members at: index. (member hasVoice and: [member isOperator not]) ifTrue: [stop _ index] ifFalse: [^ Interval from: start to: stop]]]. ^ Interval from: start to: stop! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 23:29'! aboutToBeClosed connection channelsBrowser: nil. ^ true! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 12:25'! addActionButtonsToWindow: win fractions: fractions offsets: offsets | buttons | buttons _ self buttonRow. win addMorph: buttons fullFrame: (LayoutFrame fractions: fractions offsets: offsets)! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 12:30'! addChannelListTo: win fractions: fractions offsets: offsets multiColumnMorph _ PluggableMultiColumnListMorphByItemWithHeader on: self list: #channelDescriptions selected: #channelIndex changeSelected: #channelIndex: headerStrings: #('Name' 'Pop.' 'Channel Topic'). multiColumnMorph color: Color white. multiColumnMorph headerBackColor: Color paleTan muchLighter. win addMorph: multiColumnMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets)! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 12:13'! buildMorphIn: parentMorph | buttonHeight horizMargin vertMargin | buttonHeight _ 24. horizMargin _ 12. vertMargin _ 5. self addActionButtonsToWindow: parentMorph fractions: (0 @ 0 corner: 1 @ 0) offsets: (horizMargin @ vertMargin corner: horizMargin negated @ (vertMargin + buttonHeight)). self addChannelListTo: parentMorph fractions: (0 @ 0 corner: 1 @ 1) offsets: (horizMargin @ (vertMargin + buttonHeight + vertMargin) corner: horizMargin negated @ vertMargin negated)! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 12:23'! buttonRow | aRow btn getState | aRow _ AlignmentMorph newRow beSticky. aRow color: Color paleTan. aRow clipSubmorphs: true. aRow layoutInset: 5 @ 3; borderWidth: 0; cellInset: 6. self buttonSpecs do: [:spec | getState _ spec third. getState = #none ifTrue: [getState _ nil]. btn _ PluggableButtonMorph on: self getState: getState action: spec second. Preferences alternativeWindowLook ifTrue: [btn borderWidth: 2; borderColor: #raised]. btn color: Color paleTan muchLighter; useRoundedCorners; label: spec first asString; onColor: Color transparent offColor: Color transparent. aRow addMorphBack: btn. btn setBalloonText: spec fourth]. self setRefreshButton: aRow. ^ aRow! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 12:44'! buttonSpecs ^ #(#('Join Selected' #openSelectedChannelCaptured #none 'Join the selected channel') #('Join Prompt' #openPromptedChannelCaptured #none 'Join the channel as specified in a prompter') #('Create' #createChannelCaptured #none 'Create a new channel') #('Refresh' #requestChannelList #none 'Refresh the channel list') )! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 16:41'! channelDescriptions | list topicLimit topicString | topicLimit _ 100. list _ Array new: 3. 1 to: list size do: [:index | list at: index put: OrderedCollection new]. channelList do: [:channel | (list at: 1) add: channel name. (list at: 2) add: channel numUsers printString. topicString _ channel topic. topicString size > topicLimit ifTrue: [topicString _ topicString copyFrom: 1 to: topicLimit]. (list at: 3) add: topicString]. ^ list! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 13:11'! channelListDownloadAborted refreshButton isNil ifTrue: [^ nil]. refreshButton offColor: Color transparent; label: 'Refresh'! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 12:58'! channelListDownloadCompleted refreshButton isNil ifTrue: [^nil]. refreshButton offColor: Color transparent; label: 'Refresh'! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 13:03'! channelListDownloading: count refreshButton isNil ifTrue: [^ nil]. refreshButton label: self channelListDownloadingButtonTag , ' ' , count printString; update: nil! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 12:38'! channelListDownloadingButtonTag ^'Fetching...'! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 01:04'! createChannel | channelName observer | channelName _ FillInTheBlank request: 'channel name'. channelName isEmpty ifTrue: [^ self]. observer _ IRCChannelObserverEnhanced openForChannelNamed: channelName onConnection: connection. connection addChannelObserver: observer! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 5/4/2002 14:27'! createChannelCaptured | channelName observer | channelName _ FillInTheBlank request: 'channel name'. channelName isEmpty ifTrue: [^ self]. channelName first = $# ifFalse: [channelName _ '#' , channelName]. observer _ IRCChannelObserverEnhanced openForChannelNamed: channelName onCaptiveConnection: connection. connection addChannelObserver: observer! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 12:05'! openCapturedView "We do it inside the console window!!" | paneName alignMorph | paneName _ self class tabbedPaneName. alignMorph _ AlignmentMorph newColumn beSticky; borderWidth: 0; layoutPolicy: ProportionalLayout new. alignMorph color: Color paleTan. self buildMorphIn: alignMorph. connection addSwapPane: alignMorph named: paneName hidden: false closeable: true swapModel: self! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/27/2002 00:13'! openChannelCaptured: aString | observer | observer _ IRCChannelObserverEnhanced openForChannelNamed: aString onCaptiveConnection: connection. connection addChannelObserver: observer! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/21/2002 08:49'! openMorphicView | win | win _ SystemWindow new. win setLabel: 'Enhanced Channel Listing'. win model: self. self buildMorphIn: win. win openInWorld! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 17:19'! openPromptedChannel | channelName observer | channelName _ FillInTheBlank request: 'Channel to join?'. channelName isEmpty ifTrue: [^ nil]. observer _ IRCChannelObserverEnhanced openForChannelNamed: channelName onConnection: connection. connection addChannelObserver: observer. observer updateUsers! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/27/2002 00:14'! openPromptedChannelCaptured | channelName | channelName _ FillInTheBlank request: 'Channel to join?'. channelName isEmpty ifTrue: [^ nil]. channelName first = $# ifFalse: [channelName _ '#' , channelName]. self openChannelCaptured: channelName ! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 23:35'! openSelectedChannel | channelInfo channelName observer | channelInfo _ channelList at: multiColumnMorph getCurrentSelectionIndex ifAbsent: [^ self]. channelName _ channelInfo name. observer _ IRCChannelObserverEnhanced openForChannelNamed: channelName onConnection: connection. connection addChannelObserver: observer. ! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/27/2002 00:14'! openSelectedChannelCaptured | channelInfo channelName | channelInfo _ channelList at: multiColumnMorph getCurrentSelectionIndex ifAbsent: [^ self]. channelName _ channelInfo name. self openChannelCaptured: channelName ! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/21/2002 08:56'! removeSelfFromConsole connection channelsBrowser: nil! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 12:18'! requestChannelList | btns coll | btns _ self myDependents select: [:m | m isKindOf: PluggableButtonMorph]. coll _ btns select: [:btn | btn label asString = 'Refresh']. coll isEmpty ifFalse: [coll first offColor: IRCClientColors channelListBuilding; label: 'Fetching...']. connection requestChannelList! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 13:02'! setRefreshButton: row | btns coll | btns _ row submorphs select: [:m | m isKindOf: PluggableButtonMorph]. coll _ btns select: [:btn | btn label asString = 'Refresh']. refreshButton _ coll isEmpty ifFalse: [coll first]! ]style[(18 3 4 10 4 4 30 3 2 1 11 20 4 4 3 4 14 5 2 3 18 9 4 13 3 4 23 4 7)f1b,f1cblue;b,f1,f1cblue;i,f1,f1cblue;i,f1,f1cred;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cred;,f1,f1cblue;i,f1,f1c180180104,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/21/2002 00:01'! specialFunction self inspect! ! !IRCChannelListBrowserEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/21/2002 08:55'! windowIsClosing self removeSelfFromConsole! ! !IRCChannelListBrowserEnhanced class methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 10:52'! openForCaptiveConnection: connection ^(self forConnection: connection) openCapturedView! ! !IRCChannelListBrowserEnhanced class methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 09:22'! tabbedPaneName ^'channels list'! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/10/2002 22:15'! < anIRCChannelMember "Priority is related to operators first, then voiced, then by name." self isOperator ifTrue: [anIRCChannelMember isOperator ifTrue: [^ self nick asIRCLowercase < anIRCChannelMember nick asIRCLowercase] ifFalse: [^ true]]. anIRCChannelMember isOperator ifTrue: [^ false]. self hasVoice ifTrue: [anIRCChannelMember hasVoice ifTrue: [^ self nick asIRCLowercase < anIRCChannelMember nick asIRCLowercase] ifFalse: [^ true]]. anIRCChannelMember hasVoice ifTrue: [^ false]. ^ self nick asIRCLowercase < anIRCChannelMember nick asIRCLowercase! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 10:08'! = anIRCChannelMember ^self nick = anIRCChannelMember nick! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 1/11/2003 08:49'! canChangeTopic ^ self topic or: [self operator]! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:20'! channel ^channel! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:31'! channel: aString channel _ aString! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 1/10/2003 19:44'! fromString: aString "Member cannot be both @ and + (operator and voiced) since operators already have voice." self initialize. ('+@t' includes: aString first) ifTrue: [aString first = $@ ifTrue: [self operator: true]. aString first = $+ ifTrue: [self voice: true]. aString first = $t ifTrue: [self topic: true]. self nick: (aString copyFrom: 2 to: aString size)] ifFalse: [self nick: aString]! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:39'! hasVoice ^self voice! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 10:09'! hash ^self nick hash! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 1/10/2003 19:36'! initialize self operator: false. self voice: false. self topic: false! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:38'! isOperator ^self operator! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 1/10/2003 19:45'! modeChanged: aString "+/- v/o" | plus voiced opped topicControl | aString size = 2 ifFalse: [^ nil]. plus _ (aString at: 1) = $+. voiced _ (aString at: 2) = $v. opped _ (aString at: 2) = $o. topicControl _ (aString at: 2) = $t. voiced ifTrue: [self voice: plus]. opped ifTrue: [self operator: plus]. topicControl ifTrue: [self topic: plus]! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:20'! nick ^nick! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:21'! nick: aString nick _ aString! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:21'! operator ^operator! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:21'! operator: aBoolean operator _ aBoolean! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 1/10/2003 19:29'! printOn: aStream super printOn: aStream. aStream nextPutAll: ' ("' , self nick , '")'! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 1/10/2003 19:37'! topic "Better lazy initialize since we just added this var." topic == nil ifTrue: [self topic: false]. ^ topic! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 1/10/2003 19:35'! topic: aBoolean topic _ aBoolean! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:21'! voice ^voice! ! !IRCChannelMember methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:21'! voice: aBoolean voice _ aBoolean! ! !IRCChannelMember class methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:29'! fromString: aString ^self new fromString: aString! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 1/11/2003 18:56'! aboutToBeClosed connection removeChannelObserver: self. self release. self closeLog. ^ true! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 22:48'! attemptedNicks attemptedNicks isNil ifTrue: [self initAttemptedNicks]. ^attemptedNicks! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 11:23'! calculateWidestNick "Select an arbitrary minimum." widestNick == nil ifTrue: [widestNick _ 7]. self channelUsers do: [:each | widestNick _ widestNick max: each size].! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 01:05'! channelName ^channelName! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 16:04'! channelUser self selectedUser = 0 ifTrue: [^nil]. ^ self channelUsers at: self selectedUser! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 10:19'! channelUsers ^ channel memberNames ! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 11:47'! channelUsersFormatted "The list already comes back to us with proper ordering of operators, voiced and members. We need to indicate status for some of these members in the list." | newList | newList _ self channelUsers collect: [:each | StringMorph contents: each]. channel operatorIndexes do: [:index | index > 0 ifTrue: [(newList at: index) color: IRCClientColors nickIsOperator]]. channel voicedIndexes do: [:index | index > 0 ifTrue: [(newList at: index) color: IRCClientColors nickIsVoiced]]. ^ newList! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 15:51'! checkForNickCompletionOn: morph "Use attemptedNicks to track the ones we have tried already." | typed words word prefixSentence sentence limit match | typed _ morph text. typed size < 1 ifTrue: ["leading tabs are not nick matches." ^ true]. words _ typed asString substrings. words isEmpty ifTrue: [^true]. word _ words last. nickMatchWord == nil ifTrue: [nickMatchWord _ word]. match _ self firstUnattemptedNickThatMatches: word. match isNil ifFalse: [limit _ typed size - word size. prefixSentence _ typed copyFrom: 1 to: limit. sentence _ prefixSentence , match. morph setText: sentence. morph setSelection: (sentence size + 1 to: sentence size + 1). morph hasUnacceptedEdits: true. ^ false]. ^ true! ]style[(26 5 3 61 3 53 4 5 3 5 8 5 8 1 12 36 6 4 4 5 3 5 55 4 3 5 8 13 4 3 12 13 3 4 4 5 3 4 34 4 3 5 19 5 3 5 8 4 10 14 3 5 11 1 5 5 5 8 3 14 3 5 5 5 10 8 5 5 20 8 8 1 5 8 8 1 6 5 21 4 7 5 6 4)f1b,f1cblue;b,f1,f1c130028000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c180180104,f1,f1c130028000,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c180180104,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c180180104,f1,f1cblue;i,f1,f1c180180104,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 10:28'! checkForScrollPlaybackDownOn: morph | previous | self messagePlayback rollIndexForward. previous _ self messagePlayback getIndexedMessage. previous isNil ifFalse: [morph setText: previous. morph setSelection: (previous size + 1 to: previous size + 1). morph hasUnacceptedEdits: true]. ^ false! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 10:27'! checkForScrollPlaybackUpOn: morph | previous | previous _ self messagePlayback getIndexedMessage. self messagePlayback rollIndexBack. previous isNil ifFalse: [ morph setText: previous. morph setSelection: (previous size + 1 to: previous size + 1). morph hasUnacceptedEdits: true. ]. ^ false! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/28/2002 19:41'! closeFrontMostWindow connection closeTab! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 01:15'! connection: anIRCConnection channelName: aString connection _ anIRCConnection. channel _ connection channelInfo: aString. channelName _ aString. channel subscribe: self. chatText _ Text new! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 08:17'! findMorphNamed: aString ^ self findMorphNamed: aString in: self topView! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 08:16'! findMorphNamed: aString in: parentMorph parentMorph isNil ifTrue: [^ nil]. ^ parentMorph submorphNamed: aString ifNone: []! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 17:19'! firstUnattemptedNickThatMatches: word | test | channel memberNames do: [:each | (self attemptedNicks includes: each) ifFalse: [self attemptedNicks add: each. test _ (self attemptedNicks includes: word) ifTrue: [each asIRCLowercase beginsWith: nickMatchWord asIRCLowercase] ifFalse: [each asIRCLowercase beginsWith: word asIRCLowercase]. test ifTrue: [^ each]]]. ^ nil! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 14:12'! initAttemptedNicks attemptedNicks _ Set new! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 16:46'! inputPaneHeight ^30! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 11:21'! inspectChannel self inspect! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 15:55'! ircMessageRecieved: aMessage "Ugly message specifc code in here." | sender newLine nickText messageText messageWords actionFlag | sender _ aMessage sender ifNil: [connection nick]. (sender includes: $!!) ifTrue: [sender _ sender copyFrom: 1 to: (sender indexOf: $!!) - 1]. nickText _ self prepareNickAsRequired: sender. messageText _ aMessage text. messageWords _ messageText asString substrings. actionFlag _ false. messageWords size > 1 ifTrue: [messageWords first asUppercase = ((Character value: 1) asString , 'ACTION') ifTrue: [actionFlag _ true. messageText _ messageText copyFrom: 9 to: messageText size]]. newLine _ nickText , ' : ' , messageText , String cr. "Colors..." aMessage sender isNil ifTrue: ["message is going out" newLine addAttribute: IRCClientColors transmittedMessage] ifFalse: ["message is coming in" (messageText asString asIRCLowercase includesSubString: connection nick asIRCLowercase) ifTrue: [newLine addAttribute: IRCClientColors messageContainsNick]]. actionFlag ifTrue: [newLine addAttribute: TextEmphasis italic]. self addTextToChannelPane: newLine! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 12:27'! justOpened "Placeholder"! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 11:43'! keyInterceptEndedFrom: morph morph clearInterceptActive. nickMatchWord _ nil. self initAttemptedNicks. self messagePlayback resetIndex! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 10:15'! keyInterceptedEvent: evt morph: morph "Answer true if we want the pane to have the event anyway. There are 3 classes involved here. TextMorphForInterceptingEditView intercepts the keystrokes. PluggableInterceptingTextMorph is the morph holding the text. IRCChannelObserverEnhanced (this class) decides what to do with the intercepts. Once an intercept is tripped we will recieve ALL keystrokes until we tell the text morph #clearInterceptActive" | char | char _ evt keyCharacter. char = Character tab ifTrue: [^ self checkForNickCompletionOn: morph]. char = Character arrowUp ifTrue: [^ self checkForScrollPlaybackUpOn: morph]. char = Character arrowDown ifTrue: [^ self checkForScrollPlaybackDownOn: morph]. self keyInterceptEndedFrom: morph. ^ true! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 09:58'! messagePlayback messagePlayback == nil ifTrue: [messagePlayback _ IRCMessagePlayback new]. ^messagePlayback! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 1/10/2003 21:15'! nick ^connection nick! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 12:37'! openCapturedView "We do it inside the console window!!" "open a view for interacting with this collector" | alignMorph paneName | paneName _ channel name. alignMorph _ AlignmentMorph newColumn hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 0; layoutPolicy: ProportionalLayout new. alignMorph color: Color white. self buildMorphIn: alignMorph. connection addSwapPane: alignMorph named: paneName hidden: false closeable: true swapModel: self. self justOpened! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 16:46'! openMorphicView "open a view for interacting with this collector" | win topicHeight nicksWidth inputHeight channelTextPane inputPane | win _ SystemWindow new. win setLabel: channel name. win model: self. topicHeight _ 48. nicksWidth _ 120. inputHeight _ self inputPaneHeight. self addTopicPaneTo: win fractions: (0 @ 0 corner: 1 @ 0) offsets: (0 @ 0 corner: nicksWidth negated @ topicHeight). channelTextPane _ self addChannelTextTo: win fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ topicHeight corner: nicksWidth negated @ inputHeight negated). self addUsersListTo: win fractions: (1 @ 0 corner: 1 @ 1) offsets: (nicksWidth negated @ 0 corner: 0 @ inputHeight negated). inputPane _ self addInputPaneTo: win fractions: (0 @ 1 corner: 1 @ 1) offsets: (0 @ inputHeight negated corner: 0 @ 0). channelTextPane keysInputPane: inputPane. win openInWorld. self justOpened! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 16:48'! perform: aSelector orSendTo: rcvr (self respondsTo: aSelector) ifTrue: [^ self perform: aSelector]. ^ rcvr perform: aSelector! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 14:55'! reconnect self connection: connection channelName: channelName. self changed: #chatText. self justOpened! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 01:02'! removeSelfFromConnectionObserversList connection removeChannelObserver: self! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 16:02'! selectedUser selectedUser isNil ifTrue: [self selectedUser: 0]. ^ selectedUser! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 10:36'! selectedUser: obj selectedUser _ obj. self changed: #channelUsersFormatted! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 15:55'! talkToSelectedUser self channelUser isNil ifTrue:[^nil]. self channelUser = connection nick ifTrue: [^ nil]. IRCEnhancedDirectMessagesObserver openForConnection: channel connection talkingTo: self channelUser! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 08:50'! topicStatus | count string | count _ self channelUsers size. string _ self topicStatusPrefix , count printString. status contents: string! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 08:12'! topicStatusPrefix ^ 'population: '! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 1/11/2003 08:52'! updateUsers self calculateWidestNick. self changed: #channelUsersFormatted. self topicStatus. self updateTopicPaneState! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 23:59'! whoisSelectedUser | who msg | who _ self channelUser. who isNil ifTrue: [^ nil]. msg _ IRCProtocolMessage command: 'WHOIS' arguments: (Array with: self channelName with: who asString). connection sendMessage: msg! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 16:06'! widestNick widestNick == nil ifTrue: [widestNick _ 7]. ^ widestNick! ! !IRCChannelObserverEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 01:02'! windowIsClosing self removeSelfFromConnectionObserversList ! ! !IRCChannelObserverEnhanced methodsFor: 'sending' stamp: 'sbw 3/9/2002 21:26'! acceptChannelString: aString "New message path logic." ^ connection sendChannelString: aString from: self channelName: self channelName! ! !IRCChannelObserverEnhanced methodsFor: 'sending' stamp: 'sbw 3/9/2002 10:55'! textWasSent: aText "New message logic." self messagePlayback addMessage: aText! ! !IRCChannelObserverEnhanced methodsFor: 'panel building' stamp: 'sbw 2/23/2002 01:59'! addChannelTextTo: win fractions: fractions offsets: offsets | textArea | textArea _ PluggableTextMorphHybrid on: self text: #chatText accept: nil readSelection: #chatTextSelection menu: #channelTextMenu:. win addMorph: textArea fullFrame: (LayoutFrame fractions: fractions offsets: offsets). ^ textArea! ! !IRCChannelObserverEnhanced methodsFor: 'panel building' stamp: 'sbw 4/4/2002 18:39'! addInputPaneTo: win fractions: fractions offsets: offsets | inputArea | inputArea _ PluggableInterceptingTextMorph on: self text: nil accept: #acceptChannelString:. inputArea acceptOnCR: true. inputArea addIntercept: Character tab; addIntercept: Character arrowUp; addIntercept: Character arrowDown; hideScrollBarIndefinitely. win addMorph: inputArea fullFrame: (LayoutFrame fractions: fractions offsets: offsets). ^ inputArea! ! !IRCChannelObserverEnhanced methodsFor: 'panel building' stamp: 'sbw 1/11/2003 08:44'! addTopicPaneTo: morph fractions: fractions offsets: offsets | topicMorph | topicMorph _ PluggableTextMorph on: self text: #topic accept: #changeTopic:. channel addDependent: topicMorph. topicMorph acceptOnCR: true; name: 'topicPane'; color: IRCClientColors channelTopicPane. morph addMorph: topicMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets)! ! !IRCChannelObserverEnhanced methodsFor: 'panel building' stamp: 'sbw 3/12/2002 00:21'! addUsersListTo: win fractions: fractions offsets: offsets | listView alignMorph statusHeight | listView _ PluggableListMorphSpecialHighlighting on: self list: #channelUsersFormatted selected: #selectedUser changeSelected: #selectedUser: menu: #userListMenu:. listView color: IRCClientColors userListPane. status _ TextMorph new. status contents: self topicStatusPrefix; lock. alignMorph _ AlignmentMorph newColumn hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 0; color: IRCClientColors usersPopulationPaneBackground; layoutPolicy: ProportionalLayout new. statusHeight _ 24. alignMorph addMorph: status fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 0) offsets: (6 @ 2 corner: 0 @ statusHeight)); addMorph: listView fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ statusHeight corner: 0 @ 0)). win addMorph: alignMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets). alignMorph fillWithRamp: connection paneColorRamp oriented: 16@32! ! !IRCChannelObserverEnhanced methodsFor: 'panel building' stamp: 'sbw 11/16/2002 16:46'! buildMorphIn: parentMorph | topicHeight nicksWidth inputHeight channelTextPane | topicHeight _ 48. nicksWidth _ 120. inputHeight _ self inputPaneHeight. self addTopicPaneTo: parentMorph fractions: (0 @ 0 corner: 1 @ 0) offsets: (0 @ 0 corner: nicksWidth negated @ topicHeight). channelTextPane _ self addChannelTextTo: parentMorph fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ topicHeight corner: nicksWidth negated @ inputHeight negated). self addUsersListTo: parentMorph fractions: (1 @ 0 corner: 1 @ 1) offsets: (nicksWidth negated @ 0 corner: 0 @ inputHeight negated). inPane _ self addInputPaneTo: parentMorph fractions: (0 @ 1 corner: 1 @ 1) offsets: (0 @ inputHeight negated corner: 0 @ 0). channelTextPane keysInputPane: inPane! ! !IRCChannelObserverEnhanced methodsFor: 'panel building' stamp: 'sbw 3/1/2002 12:56'! channelTextMenu: aMenu aMenu addList: #(#('find (f)' #find) #('find again (g)' #findAgain) #('set search string (h)' #setSearchString) #('copy (c)' #copySelection) #('inspect channel observer' #inspectChannel) ). connection frontMostPaneIsCloseable ifTrue: [aMenu addList: #(#('close' #closeFrontMostWindow) )]. ^ aMenu! ! !IRCChannelObserverEnhanced methodsFor: 'panel building' stamp: 'sbw 2/20/2002 01:07'! statusMenu: aMenu "Treat the menu like a button." self updateUsers. ^ aMenu! ! !IRCChannelObserverEnhanced methodsFor: 'panel building' stamp: 'sbw 3/10/2002 17:29'! userListMenu: aMenu "Build a context sensitive menu for the users list." | selected | selected _ self channelUser. selected isNil ifFalse: [selected = connection nick ifFalse: [aMenu add: 'talk to selected user' action: #talkToSelectedUser]. aMenu add: 'who is selected user?' action: #whoisSelectedUser]. ^ aMenu! ! !IRCChannelObserverEnhanced methodsFor: 'channel pane text' stamp: 'sbw 1/11/2003 13:41'! addTextToChannelPane: aString | stringToAppend | (connection option: #logChannelTraffic) ifTrue: [self logChannelTraffic: aString]. stringToAppend _ (connection option: #usemIRCColors) ifTrue: [self class convertTextUsingMIRCColors: aString] ifFalse: [aString]. chatText _ chatText , stringToAppend. chatText size > 2000 ifTrue: [chatText _ chatText copyFrom: chatText size - 1000 to: chatText size]. self changed: #chatText. connection possibleTabStateChange: self channelName! ! !IRCChannelObserverEnhanced methodsFor: 'channel pane text' stamp: 'sbw 3/5/2002 16:40'! addTextToChannelPane: aString usingUnpreparedNick: sender | nickText | nickText _ self prepareNickAsRequired: sender. self addTextToChannelPane: nickText, ' : ', aString! ! !IRCChannelObserverEnhanced methodsFor: 'channel pane text' stamp: 'sbw 3/2/2002 10:24'! clearTextPane chatText _ Text new. self changed: #chatText! ! !IRCChannelObserverEnhanced methodsFor: 'channel pane text' stamp: 'sbw 3/5/2002 15:55'! prepareNickAsRequired: aTextOrString "We answer a Text" | nickText pad | nickText _ aTextOrString asText. nickText size < self widestNick ifTrue: [pad _ String new: widestNick - nickText size. nickText _ (pad , nickText) asText]. nickText addAttribute: (TextFontReference toFont: (TextStyle named: #DefaultFixedTextStyle) defaultFont). ^ nickText! ! !IRCChannelObserverEnhanced methodsFor: 'topic pane' stamp: 'sbw 1/11/2003 08:55'! canChangeTopic | nick user | nick _ self nick. user _ channel findMember: nick. ^user canChangeTopic! ! !IRCChannelObserverEnhanced methodsFor: 'topic pane' stamp: 'sbw 1/11/2003 08:55'! changeTopic: aText self canChangeTopic ifTrue: [channel changeTopic: aText]! ! !IRCChannelObserverEnhanced methodsFor: 'topic pane' stamp: 'sbw 1/11/2003 09:26'! disableTopicEdits | pane color | pane _ self findTopicMorph. color _ IRCClientColors channelTopicPane. pane color: color. pane replaceSetTextSelector: nil! ! !IRCChannelObserverEnhanced methodsFor: 'topic pane' stamp: 'sbw 1/11/2003 09:26'! enableTopicEdits | pane color | pane _ self findTopicMorph. color _ IRCClientColors channelTopicPaneEnabled. pane color: color. pane replaceSetTextSelector: #changeTopic:! ! !IRCChannelObserverEnhanced methodsFor: 'topic pane' stamp: 'sbw 1/11/2003 09:13'! findTopicMorph | morphs | morphs _ self myDependents select: [:m | m isMorph]. ^ morphs detect: [:m | m knownName = 'topicPane']! ]style[(14 3 7 4 6 3 4 27 3 2 1 14 6 12 3 2 1 13 11 1)f1b,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cred;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cred;,f1,f1cblue;i,f1,f1c000198055,f1! ! !IRCChannelObserverEnhanced methodsFor: 'topic pane' stamp: 'sbw 1/11/2003 08:33'! topic ^channel topic! ! !IRCChannelObserverEnhanced methodsFor: 'topic pane' stamp: 'sbw 1/11/2003 08:56'! updateTopicPaneState self canChangeTopic ifTrue: [self enableTopicEdits] ifFalse: [self disableTopicEdits]! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 19:34'! channelNameWithoutSymbol | string | string _ self channelName. string first = $# ifTrue: [string _ string copyFrom: 2 to: string size]. ^string! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 18:57'! closeLog logFile == nil ifFalse: [logFile close]! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 20:04'! ensureIRCLogFileForChannel | fName dir exists path | self ensureIRCLogsFolder. fName _ self logFileNameForChannel. dir _ self logsFolder. exists _ dir fileExists: fName. path _ dir fullPathFor: fName. logFile _ CrLfFileStream fileNamed: path. exists ifTrue: [logFile cr; nextPutAll: TimeStamp current printString; cr; flush]! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 19:05'! ensureIRCLogsFolder FileDirectory default assureExistenceOfPath: self logsFolderName! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 20:06'! logChannelTraffic: aText self logFile nextPutAll: aText asString; flush! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 18:55'! logFile logFile == nil ifTrue: [self ensureIRCLogFileForChannel]. ^ logFile! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 19:37'! logFileNameForChannel ^ self logsFolder checkName: self channelName , '.txt' fixErrors: true! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 19:18'! logsFolder ^FileDirectory default directoryNamed: self logsFolderName! ! !IRCChannelObserverEnhanced methodsFor: 'logging' stamp: 'sbw 1/11/2003 18:51'! logsFolderName ^'irclogs'! ! !IRCChannelObserverEnhanced class methodsFor: 'private' stamp: 'sbw 11/21/2002 19:16'! convertTextUsingMIRCColors: aTextOrString locs: locs | cr colors result posn piece attr | cr _ Character cr. result _ TextStream on: '' asText. posn _ 1. attr _ TextColor black. locs keys asSortedCollection do: [:key | piece _ aTextOrString copyFrom: posn to: key - 1. result withAttribute: attr do: [result nextPutAll: piece]. (locs at: key) = cr ifTrue: [attr _ TextColor black. result nextPut: (locs at: key). posn _ key + 1] ifFalse: [(self isNextCharacterANumberIn: aTextOrString startingAt: key) ifTrue: [colors _ self extractColorCodesFrom: aTextOrString at: key. posn _ key + 1 + (colors at: #skipped). (colors at: #foreground) == nil ifFalse: [attr _ IRCClientColors mircColorFor: (colors at: #foreground) asNumber]. (colors at: #background) == nil ifFalse: [(colors at: #background)]] ifFalse: [attr _ TextColor black. posn _ key + 1]]]. posn < aTextOrString size ifTrue: [piece _ aTextOrString copyFrom: posn to: aTextOrString size. result nextPutAll: piece]. ^ result contents! ! !IRCChannelObserverEnhanced class methodsFor: 'private' stamp: 'sbw 11/21/2002 18:15'! extractColorCodesFrom: aText at: anInteger "Should find any of: nABC nnABC n,nABC nn,nABC nn,nnABC (where ABC is following text containing letters but not beginning with a digit if only one value of 'n' is found. The ABC could start with a comma). Answer a dictionary containing settings found for foreground and background colors. If no background color setting is found the value will be returned as nil. The amount of characters we skip are included in the result too." | result start stop extract commaPos limit foregroundString backgroundString | result _ Dictionary new. result at: #foreground put: nil. result at: #background put: nil. start _ anInteger. stop _ start. [(self isNextCharacterACommaIn: aText startingAt: stop) or: [self isNextCharacterANumberIn: aText startingAt: stop]] whileTrue: [stop _ stop + 1]. stop = start ifFalse: [extract _ aText copyFrom: start + 1 to: stop. commaPos _ extract indexOf: $,. limit _ commaPos = 0 ifTrue: [stop] ifFalse: [start + commaPos - 1]. foregroundString _ aText copyFrom: start + 1 to: limit. backgroundString _ commaPos = 0 ifFalse: [aText copyFrom: limit + 2 to: stop]]. foregroundString isNil ifFalse: [result at: #foreground put: foregroundString]. backgroundString isNil ifFalse: [result at: #background put: backgroundString]. result at: #skipped put: stop - start. ^ result! ]style[(23 5 5 9 3 462 3 75 4 6 3 10 7 6 5 11 6 3 3 6 5 11 6 3 3 5 3 9 3 4 3 5 5 4 26 5 13 4 9 4 27 5 13 4 17 4 3 4 3 1 4 4 3 5 13 7 3 5 11 5 3 1 5 4 5 8 3 7 10 2 5 5 3 8 3 1 16 4 26 8 3 1 6 16 3 5 11 9 5 5 5 16 3 8 3 1 17 5 19 1 5 4 5 16 19 6 5 11 6 16 4 16 19 6 5 11 6 16 4 6 5 8 6 4 3 5 5 6)f1b,f1cblue;b,f1b,f1cblue;b,f1,f1c138036000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c024176000,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c024176000,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i! ! !IRCChannelObserverEnhanced class methodsFor: 'private' stamp: 'sbw 11/21/2002 18:08'! isNextCharacterACommaIn: aText startingAt: anInteger "Actually checks if the next character is a comma AND if the character after that is a digit." | index commaResult | anInteger < aText size ifFalse: [^ false]. index _ anInteger + 1. commaResult _ (aText at: index) = $,. commaResult ifFalse: [^ false]. (anInteger + 1) < aText size ifFalse: [^ false]. ^ self isNextCharacterANumberIn: aText startingAt: anInteger + 1! ]style[(25 5 13 9 3 100 3 18 4 9 3 5 20 5 4 5 3 9 3 1 3 11 4 5 5 5 8 2 3 11 15 5 5 9 3 1 4 5 20 5 6 4 27 5 13 9 3 1)f1b,f1cblue;b,f1b,f1cblue;b,f1,f1c138036000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000! ! !IRCChannelObserverEnhanced class methodsFor: 'private' stamp: 'sbw 11/19/2002 18:00'! isNextCharacterANumberIn: aText startingAt: anInteger | index | index _ anInteger + 1. ^(aText at: index) isDigit! ! !IRCChannelObserverEnhanced class methodsFor: 'irc color text support' stamp: 'sbw 11/19/2002 17:56'! convertTextUsingMIRCColors: aTextOrString "We need to answer Text having the colors converted as required. A color sequence ends with each line of text or an empty trap character. The trap character having a 1 or 2 digit numerical value directly following is the mIRC code for a foreground color change. An optional comma directly following the number means there is also a background color parameter directly after the comma. Foreground and background colors stick, once set, until they are terminated as described above. You can set both the foreground and background color, change just the foreground color later and then set both again if we receive a message formatted this way. Note there is no way to just change the background color alone. It's possible we may have multiple lines in the given text so we'll have to watch for end of line characters since colors reset upon a new line." | trapCharacter cr locs char | trapCharacter _ IRCClientColors colorTrapCharacter. cr _ Character cr. locs _ Dictionary new. 1 to: aTextOrString size do: [:index | char _ aTextOrString at: index. (char = trapCharacter or: [char = cr]) ifTrue: [locs at: index put: char]]. ^ self convertTextUsingMIRCColors: aTextOrString locs: locs! ! !IRCChannelObserverEnhanced class methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 23:43'! openForChannelNamed: channelName onCaptiveConnection: connection ^ (super new connection: connection channelName: channelName) openCapturedView! ! !IRCClientColors class methodsFor: 'mIRC colors' stamp: 'sbw 11/17/2002 18:59'! colorTrapCharacter ColorTrapCharacter == nil ifTrue: [ColorTrapCharacter _ Character value: 3]. ^ColorTrapCharacter! ! !IRCClientColors class methodsFor: 'mIRC colors' stamp: 'sbw 11/21/2002 19:18'! exampleTest "IRCClientColors exampleTest" | testMessage stream trap part text | testMessage _ 'This should be a simple string. And this is the second line.'. stream _ WriteStream on: ''. trap _ IRCClientColors colorTrapCharacter. part _ testMessage copyFrom: 1 to: 18. stream nextPutAll: part. stream nextPut: trap. stream nextPutAll: '04'. part _ testMessage copyFrom: 19 to: 38. stream nextPutAll: part. stream nextPut: trap. stream nextPutAll: '02,09'. part _ testMessage copyFrom: 39 to: 48. stream nextPutAll: part. stream nextPut: trap. part _ testMessage copyFrom: 49 to: testMessage size. stream nextPutAll: part. text _ IRCChannelObserverEnhanced convertTextUsingMIRCColors: stream contents. (Workspace new contents: text) openLabel: 'test'! ! !IRCClientColors class methodsFor: 'mIRC colors' stamp: 'sbw 11/21/2002 20:26'! initializeTextColorMap "Be sure to reinitialize the color map if you make edits here." "IRCClientColors initializeTextColorMap" TextColorMap _ Dictionary new. TextColorMap at: 0 put: (TextColor color: Color white); at: 1 put: (TextColor color: Color black); at: 2 put: (TextColor color: Color blue darker); at: 3 put: (TextColor color: Color green darker); at: 4 put: (TextColor color: Color red darker); at: 5 put: (TextColor color: Color brown); at: 6 put: (TextColor color: Color magenta darker); at: 7 put: (TextColor color: Color orange darker); at: 8 put: (TextColor color: Color yellow); at: 9 put: (TextColor color: Color lightGreen); at: 10 put: (TextColor color: Color cyan darker); at: 11 put: (TextColor color: Color cyan); at: 12 put: (TextColor color: Color lightBlue); at: 13 put: (TextColor color: Color magenta); at: 14 put: (TextColor color: Color darkGray); at: 15 put: (TextColor color: Color gray)! ! !IRCClientColors class methodsFor: 'mIRC colors' stamp: 'sbw 11/17/2002 18:11'! mircColorFor: anInteger | factoredColorCode | anInteger == 99 ifTrue: [^ TextColor color: Color transparent] ifFalse: [factoredColorCode _ anInteger \\ 16. ^ self textColorMap at: factoredColorCode]! ! !IRCClientColors class methodsFor: 'mIRC colors' stamp: 'sbw 11/17/2002 17:51'! textColorMap TextColorMap == nil ifTrue: [self initializeTextColorMap]. ^ TextColorMap! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/24/2002 00:29'! channelListBuilding "Color to use to indicate that the channel list is building." ^Color lightGreen! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/25/2002 09:16'! channelTopicPane ^ Color veryVeryLightGray! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 1/10/2003 20:36'! channelTopicPaneEnabled ^ Color white! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/24/2002 00:24'! connected "Color of button to show connection is active." ^ Color lightCyan muchLighter! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/24/2002 00:23'! connectionDropped "The button color to show when an active connection is detected to have dropped. " ^ Color paleRed! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 3/1/2002 22:49'! consolePaneBackground ^Color white darker! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/24/2002 00:16'! messageArrived "The color used to highlight the border around the tab of a pane if text was added while we were looking at another pane." ^Color lightRed! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/24/2002 10:47'! messageContainsNick "Color of messages that we receive which contain our Nick." ^ TextColor new color: Color magenta darker! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 3/1/2002 11:47'! nickIsOperator ^ Color red! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 3/1/2002 13:09'! nickIsVoiced ^ Color blue! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/24/2002 00:19'! swappedPaneInFocus "The color for the tab of the swapped pane currently in focus." ^Color lightYellow! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/24/2002 10:32'! transmittedMessage "Color of messages that we send." ^ TextColor blue! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/25/2002 09:07'! userListPane "The list pane showing users on a channel." ^ Color veryVeryLightGray! ! !IRCClientColors class methodsFor: 'colors' stamp: 'sbw 2/24/2002 00:32'! usersPopulationPaneBackground "The color of the population pane background." ^Color white! ! !IRCClientSounds class methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 17:55'! genericPrivateMessageArrived Smalltalk beep! ! !IRCClientSounds class methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 17:55'! specificPrivateMessageArrived "To a private chat, not generic." Smalltalk beep! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/28/2002 07:25'! addPane: pane toSwapper: swapper paneName: aString default: default swapper addPane: pane named: aString hidden: default not closeable: false swapModel: self. default ifTrue: [swapper defaultPaneName: aString]! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 22:17'! buildSwapper ^ TabbedSwappingControl newSwapControlPaneOrientation: #bottom showMover: false! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 17:01'! connection: conn connection _ conn! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 10:44'! connectionStateChanged connectedState _ connection connected. self showConfigDenial: connectedState. self disableProfileSelectButton: connectedState. ! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 01:32'! hideWindow self topView hide! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 15:34'! initialExtent ^ 500 @ 200! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 11:56'! okToChange (self topView submorphs select: [:m | m isKindOf: PluggableTextMorph]) do: [:textMorph | textMorph hasUnacceptedEdits ifTrue: [^false]]. ^true! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 07:55'! openDialog "open a dialogue for making new connections" | dialogue swappingControl | dialogue _ SystemWindow new. dialogue model: self. swappingControl _ self buildSwapper. self buildConfigurationPane. self addPane: configurationPane toSwapper: swappingControl paneName: 'Current Configuration' default: true. self addPane: self buildOptionsPane toSwapper: swappingControl paneName: 'Current Options' default: false. self addPane: self buildProfilesPane toSwapper: swappingControl paneName: 'Profiles' default: false. dialogue addMorph: swappingControl frame: (0 @ 0 extent: 1 @ 1). swappingControl goBehind. dialogue setLabel: 'setup for IRC cllient'. dialogue openInWorld! ! !IRCConnectionDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 21:37'! showWindow self topView show! ! !IRCConnectionDialog methodsFor: 'configuration' stamp: 'sbw 3/3/2002 14:06'! addConfigurationControlsTo: aPane | y yDelta descMorph textEntry specs bottomY bottomYfrac | y _ 0. specs _ self configurationButtonSpecs. yDelta _ self initialExtent y - 47 // (specs size / 2). "subtract an ammount before divding to estimate for titlebar and window borders." specs pairsDo: [:desc :meth | descMorph _ self buttonMorphForConfigurationTag: desc selector: meth. descMorph color: Color white. textEntry _ self textEntryMorphForConfigurtionSelector: meth. textEntry color: Color white. bottomYfrac _ meth = specs last ifTrue: [1] ifFalse: [0]. bottomY _ meth = specs last ifTrue: [0] ifFalse: [y + yDelta]. aPane addMorph: descMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 0.3 @ bottomYfrac) offsets: (0 @ y corner: 0 @ bottomY)). aPane addMorph: textEntry fullFrame: (LayoutFrame fractions: (0.3 @ 0 corner: 1 @ bottomYfrac) offsets: (0 @ y corner: 0 @ bottomY)). y _ bottomY]! ! !IRCConnectionDialog methodsFor: 'configuration' stamp: 'sbw 3/4/2002 09:45'! buildConfigurationDenialPane | deniedMorph | denialPane _ AlignmentMorph newColumn hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 0; layoutPolicy: ProportionalLayout new. denialPane color: Color transparent. deniedMorph _ self denialTrueTypeMorph. denialPane addMorph: deniedMorph fullFrame: (LayoutFrame fractions: (0.1 @ 0.1 corner: 0.9 @ 0.9) offsets: (0 @ 0 corner: 0 @ 0))! ! !IRCConnectionDialog methodsFor: 'configuration' stamp: 'sbw 3/4/2002 08:41'! buildConfigurationPane configurationPane _ AlignmentMorph newColumn hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 0; layoutPolicy: ProportionalLayout new. self addConfigurationControlsTo: configurationPane. self buildConfigurationDenialPane. self connectionStateChanged! ! !IRCConnectionDialog methodsFor: 'configuration' stamp: 'sbw 3/4/2002 09:46'! buttonMorphForConfigurationTag: desc selector: meth | descMorph | descMorph _ PluggableButtonMorph on: connection getState: nil action: (meth , 'Button') asSymbol. descMorph hResizing: #spaceFill; vResizing: #spaceFill; onColor: Color white offColor: Color white; label: desc. ^ descMorph! ! !IRCConnectionDialog methodsFor: 'configuration' stamp: 'sbw 3/3/2002 13:49'! configurationButtonSpecs "tag, selector" ^ #('server' #server 'port' #portAsString 'nick' #nick 'username' #userName 'full name' #fullName )! ! !IRCConnectionDialog methodsFor: 'configuration' stamp: 'sbw 3/4/2002 09:44'! denialTrueTypeMorph | ttMorph | ttMorph _ TTSampleStringMorph font: TTFontDescription default. ttMorph string: 'Connected'; color: (Color red alpha: 0.3); borderColor: Color transparent. ^ ttMorph! ! !IRCConnectionDialog methodsFor: 'configuration' stamp: 'sbw 3/4/2002 09:19'! showConfigDenial: boolean "boolean is true if we are connected." self myDependents isNil ifFalse: [ boolean ifTrue: [configurationPane addMorph: denialPane fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ 0 corner: 0 @ 0))] ifFalse: [denialPane delete]]! ! !IRCConnectionDialog methodsFor: 'configuration' stamp: 'sbw 3/3/2002 13:53'! textEntryMorphForConfigurtionSelector: meth | textEntry | textEntry _ PluggableTextMorph on: connection text: meth accept: (meth , ':') asSymbol. textEntry extent: 200 @ 20; color: (Color r: 1.0 g: 1.0 b: 0.599); acceptOnCR: true. ^ textEntry! ! !IRCConnectionDialog methodsFor: 'options' stamp: 'sbw 3/3/2002 11:31'! buildOptionsPane | aPane button str | aPane _ AlignmentMorph newColumn beSticky; borderWidth: 1. aPane color: Color white. str _ StringMorph contents: 'Options are for all console windows' font: (StrikeFont familyName: 'NewYork' size: 12). str emphasis: 1. aPane addMorphBack: str. aPane addTransparentSpacerOfSize: 0 @ 8. connection options keys asSortedCollection do: [:each | button _ self createOptionButtonForOption: each. aPane addMorphBack: button]. ^ aPane! ! !IRCConnectionDialog methodsFor: 'options' stamp: 'sbw 3/3/2002 12:01'! createOptionButtonForOption: aSymbol | button wrapper str | wrapper _ AlignmentMorph newRow height: 24. wrapper color: Color transparent. wrapper hResizing: #spaceFill; vResizing: #shrinkWrap. button _ UpdatingThreePhaseButtonMorphEnhanced checkBox. wrapper addTransparentSpacerOfSize: 2 @ 0. wrapper addMorphBack: button. button target: connection; actionSelector: #toggleOption:; arguments: (Array with: aSymbol); getSelector: #option:; getArgument: aSymbol. wrapper addTransparentSpacerOfSize: 2 @ 0. str _ StringMorph contents: aSymbol asString font: (StrikeFont familyName: 'NewYork' size: 12). wrapper addMorphBack: str. ^ wrapper! ]style[(29 7 4 19 4 7 3 14 16 2 3 7 8 5 15 7 12 10 16 11 3 6 3 37 12 7 29 1 3 1 3 7 15 6 3 6 9 10 21 14 19 5 7 7 19 8 18 7 3 7 29 1 3 1 3 3 3 11 15 7 21 10 13 9 7 2 4 7 15 3 5 7)f1b,f1cblue;b,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c180180104,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c180180104,f1,f1c180180104,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c180180104,f1,f1c180180104,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c180180104,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c180180104,f1,f1cblue;i,f1,f1cblue;i,f1,f1c180180104,f1,f1c180180104,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c180180104,f1,f1c180180104,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 07:51'! addProfile | dlg | dlg _ IRCProfileDialog new. dlg parentDialog: self. dlg referenceConnection: connection. self hideWindow. dlg openForAddWithOptions: connection options copy! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 00:49'! addProfile: aConnectionProfile connection class addProfile: aConnectionProfile. self changed: #profileDescriptions! ]style[(12 18 33 18 3 4 10 20)f1b,f1cblue;b,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c180180104! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 10:48'! buildProfileActionButtons | alignMorph btn | alignMorph _ AlignmentMorph newRow. alignMorph beSticky; layoutInset: 5 @ 3; cellInset: 6; clipSubmorphs: true; borderWidth: 0; color: Color paleTan. self profileButtonSpecs do: [:spec | btn _ PluggableButtonMorph on: self getState: nil action: spec second. Preferences alternativeWindowLook ifTrue: [btn borderWidth: 2; borderColor: #raised]. btn label: spec first. btn color: self profileActionButtonColor. btn onColor: Color white offColor: self profileActionButtonColor. alignMorph addMorphBack: btn. btn setBalloonText: spec fourth. btn setNameTo: spec third]. ^ alignMorph! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/3/2002 18:49'! buildProfilesList | listMorph | listMorph _ PluggableMultiColumnListMorphByItemWithHeader on: self list: #profileDescriptions selected: #profilesIndex changeSelected: #profilesIndex: headerStrings: #('Profile Name' 'Nick' 'Server' ). listMorph color: Color white. listMorph headerBackColor: Color paleTan muchLighter. ^ listMorph! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/3/2002 15:24'! buildProfilesPane | aPane | aPane _ AlignmentMorph newColumn beSticky; borderWidth: 0. aPane color: Color paleTan; layoutPolicy: ProportionalLayout new. aPane addMorph: self buildProfileActionButtons fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 0) offsets: (self horizPaneMargin @ self vertPaneMargin corner: self horizPaneMargin negated @ (self vertPaneMargin + self profileButtonHeight))). aPane addMorph: self buildProfilesList fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (self horizPaneMargin @ (self vertPaneMargin + self profileButtonHeight + self vertPaneMargin) corner: self horizPaneMargin negated @ self vertPaneMargin negated)). ^ aPane! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 01:08'! deleteProfile | key | profilesIndex == nil ifTrue: [^ nil]. key _ profilesIndex. connection class removeProfileNamed: key. self changed: #profileDescriptions! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 10:54'! disableProfileSelectButton: boolean | morph | self topView isNil ifTrue: [^ nil]. morph _ self topView submorphNamed: 'selectProfile' ifNone: []. morph isNil ifTrue: [^ nil]. boolean ifTrue: [morph offColor: Color veryVeryLightGray] ifFalse: [morph offColor: self profileActionButtonColor]! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'BJP 4/19/2002 13:37'! editProfile | key profile dlg | profilesIndex == nil ifTrue: [^ nil]. key _ profilesIndex. profile _ connection class getProfileNamed: key. profile isNil ifTrue: [^ self inform: 'Could not find the selected profile']. dlg _ IRCProfileDialog new. dlg parentDialog: self. dlg referenceConnection: connection. self hideWindow. dlg openDialogOn: profile "disableAdvanced: true"! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/5/2002 00:24'! editedProfile: aProfile self changed: #profileDescriptions. "Check if we need to fix the key to agree with the name of the profile." connection class checkKeyForProfileNamed: aProfile name! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/3/2002 15:35'! horizPaneMargin ^ 12! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 10:48'! profileActionButtonColor ^Color paleTan muchLighter! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/3/2002 14:18'! profileButtonHeight ^26! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/5/2002 19:21'! profileButtonSpecs "tag, selector, button name, help text" ^ #(#('add' #addProfile 'addProfile' 'Add a new profile') #('delete' #deleteProfile 'deleteProfile' 'Delete the selected profile') #('edit' #editProfile 'editProfile' 'Edit the selected profile') #('select' #selectProfile 'selectProfile' 'Load the selected profile into your current configuration') #('Save current...' #saveCurrentToProfile 'saveCurrent' 'Save your current configuration to a new profile') )! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 11:22'! profileDescriptions | list | list _ Array new: 3. 1 to: list size do: [:index | list at: index put: OrderedCollection new]. (connection class profiles asSortedCollection: [:a :b | a name < b name]) do: [:profile | (list at: 1) add: profile name. (list at: 2) add: profile nick. (list at: 3) add: profile server profileListString]. ^ list! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/3/2002 15:20'! profilesIndex ^profilesIndex! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 01:04'! profilesIndex: string profilesIndex _ string. self changed: #profilesIndex! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/5/2002 19:31'! saveCurrentToProfile "Ask the user for a new profile name and then save the current setup to that profile." | newName newProfile keepLooping | [newName _ FillInTheBlank request: 'New profile name?'. newName isEmpty ifTrue: [^ nil]. keepLooping _ connection class hasProfileNamed: newName. keepLooping ifTrue: [self inform: 'That profile name is already in use.']. keepLooping] whileTrue. newProfile _ IRCConnectionProfile fromConnection: connection. newProfile name: newName. connection class addProfile: newProfile. self changed: #profileDescriptions! ]style[(20 2 89 3 19 17 7 3 14 10 19 3 7 22 3 18 10 24 7 24 4 9 38 29 10 3 20 17 10 3 10 7 7 3 10 19 10 3 4 10 20)f1b,f1,f1c132030000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c182182106,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c182182106,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c182182106! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/4/2002 11:01'! selectProfile | key profile | connectedState ifTrue: [^ nil]. key _ profilesIndex. profile _ connection class getProfileNamed: key. profile isNil ifTrue: [^self inform: 'Could not find selected profile.']. connection loadProfile: profile! ! !IRCConnectionDialog methodsFor: 'profiles' stamp: 'sbw 3/3/2002 15:13'! vertPaneMargin ^ 5! ! !IRCConnectionDialog class methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 17:01'! on: anIRCConnection ^ self new connection: anIRCConnection! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 23:28'! aboutToBeClosed "This is the MOTD pane!!" motdPane isNil ifFalse: [motdPane _ nil]. ^ true! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 01:00'! addChannelObserver: obj self channelObservers add: obj! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 16:31'! channelInfo: channelName "return cached info on a channel" | lChannelName | lChannelName _ channelName asIRCLowercase. ^ subscribedChannels at: lChannelName ifAbsent: ["no info available--create and return a skeleton" subscribedChannels at: lChannelName put: (IRCChannelInfoEnhanced forChannelNamed: channelName onConnection: self)]! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 12:25'! channelMembersLoaded: aChannelInfo (self channelObservers select: [:each | each channelName = aChannelInfo name]) do: [:obs | obs updateUsers]! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 01:00'! channelObservers channelObservers isNil ifTrue: [channelObservers _ OrderedCollection new]. ^ channelObservers! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 20:50'! channelsBrowser ^channelsBrowser! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 20:51'! channelsBrowser: aIRCChannelListBrowserEnhanced channelsBrowser _ aIRCChannelListBrowserEnhanced! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 20:54'! channelsBrowserShouldClose | window coll | self channelsBrowser isNil ifTrue: [^ nil]. coll _ self channelsBrowser dependents select: [:x | x isKindOf: SystemWindow]. coll isEmpty ifTrue: [^ nil]. window _ coll first. window isInWorld ifTrue: [window delete]. self channelsBrowser: nil! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 10:38'! clearTextPane consoleText _ Text new. self changed: #consoleText! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 10:40'! connect super connect. "self updateButtonNamed: 'connect' newTag: 'disconnect'." self topView setLabel: self baseWindowTitle , ' [' , self nick , ' @ ' , self server , ']'! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 09:27'! connectDialog ^connectDialog! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 09:27'! connectDialog: obj connectDialog _ obj! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 09:28'! connectDialogShouldClose | window coll | self connectDialog isNil ifTrue: [^ nil]. coll _ self connectDialog dependents select: [:x | x isKindOf: SystemWindow]. coll isEmpty ifTrue: [^ nil]. window _ coll first. window isInWorld ifTrue: [window delete]. self connectDialog: nil! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 12:10'! connectToggle | reconnect | reconnect _ false. self connected ifTrue: [self manualDisconnect] ifFalse: [ dropped isNil ifFalse: [ dropped ifTrue: [reconnect _ true] ]. dropped _ false. self connect. reconnect ifTrue: [self reconnectBecauseDropped]]! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 15:38'! connected ^ self isConnected! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 14:32'! consoleMessagePlayback consoleMessagePlayback == nil ifTrue: [consoleMessagePlayback _ IRCMessagePlayback new]. ^ consoleMessagePlayback! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 10:40'! disconnect super disconnect. "self updateButtonNamed: 'connect' newTag: 'connect'." self topView isNil ifFalse: [self topView setLabel: self baseWindowTitle]! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:37'! fullNameButton "From dialog" ^ self! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 23:25'! initialize super initialize. channelObservers _ OrderedCollection new. ! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/26/2002 22:27'! leave: channelName "leave a channel" self sendMessage: (IRCProtocolMessage command: 'part' arguments: (Array with: channelName with: self quitMessage))! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 11:13'! loadProfile: anIRCConnectionProfile "Don't do this while connected." self connected ifTrue: [^ nil]. self populateOptionsFrom: anIRCConnectionProfile options; server: anIRCConnectionProfile server address; portAsString: anIRCConnectionProfile server port asString; nick: anIRCConnectionProfile nick; userName: anIRCConnectionProfile userName; fullName: anIRCConnectionProfile fullName. self changed: #server; changed: #portAsString; changed: #nick; changed: #userName; changed: #fullName! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 09:55'! manualDisconnect self quit. dropped _ nil. self disconnect. self closeTabs. ! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 16:24'! msgObserver ^msgObserver! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 16:25'! msgObserver: aIRCDirectMessagesObserver msgObserver _ aIRCDirectMessagesObserver! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 20:53'! msgObserverShouldClose | window coll | self msgObserver isNil ifTrue: [^ nil]. coll _ self msgObserver dependents select: [:x | x isKindOf: SystemWindow]. coll isEmpty ifTrue: [^ nil]. window _ coll first. window isInWorld ifTrue: [window delete]. self msgObserver: nil! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 12:14'! newConsole "This is attached to the new console button. One difference between doing this and just opening up a new console from the World menu is that this action will copy the options forward." | consoleModel | consoleModel _ self class new. consoleModel populateOptionsFrom: self options. consoleModel openWindows! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:37'! nickButton "From dialog" ^ self! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 08:01'! notifyConfigDialogConnectState connectDialog isNil ifTrue: [^ nil] ifFalse: [connectDialog connectionStateChanged]! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 22:46'! openAsMorph "build views" | win | win _ SystemWindow labelled: self baseWindowTitle. win model: self. self buildMorphIn: win. win openInWorld. ! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 23:06'! openChannelList "open a view on the list of channels" self channelsBrowser isNil ifTrue: [self channelsBrowser: (IRCChannelListBrowserEnhanced openForCaptiveConnection: self)] ifFalse: [swapper showPaneNamed: IRCChannelListBrowserEnhanced tabbedPaneName]! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 14:50'! openConnectionDialogue self connectDialog: (IRCConnectionDialog on: self). self connectDialog openDialog. ! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 16:27'! openDirectMessagesObserver self msgObserver: (IRCEnhancedDirectMessagesObserver openForConnection: self)! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 23:31'! openMotd "We do it inside the console window!!" "open a view on the MOTD" | paneName alignMorph textView updateButton buttonHeight | buttonHeight _ 22. motdPane isNil ifFalse: [^ nil]. paneName _ 'MOTD'. alignMorph _ AlignmentMorph newColumn hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 2; layoutPolicy: ProportionalLayout new. textView _ PluggableTextMorph on: self text: #motd accept: nil. alignMorph addMorph: textView fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ 0 corner: 0 @ buttonHeight negated)). updateButton _ PluggableButtonMorph on: self getState: nil action: #requestMotd. updateButton hResizing: #spaceFill; vResizing: #spaceFill; label: 'update'. alignMorph addMorph: updateButton fullFrame: (LayoutFrame fractions: (0 @ 1 corner: 1 @ 1) offsets: (0 @ buttonHeight negated corner: 0 @ 0)). alignMorph color: Color white. self addSwapPane: alignMorph named: paneName hidden: false closeable: true swapModel: self. motdPane _ alignMorph! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/20/2002 22:04'! openWindows self openView; openDirectMessagesObserver! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:53'! port ^port! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:36'! portAsStringButton "From dialog" ^ self! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/26/2002 22:55'! quit "tell the server we are leaving" "self addCrLfAndSendString: 'QUIT :' , self quitMessage." self sendMessage: (IRCProtocolMessage command: 'quit' arguments: (Array with: self quitMessage))! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/26/2002 22:26'! quitMessage ^'Using Squeak IRC Client. (See http://www.squeak.org)'! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 23:15'! reconnectBecauseDropped "The user has manually reconnected us because of a server connection drop. See if we can restore some things." | panes | panes _ swapper allSwappedPaneNamesWithModels. panes do: [:each | (each isKindOf: IRCChannelObserverEnhanced) ifTrue: [self reconnectChannelChat: each]. (each isKindOf: IRCEnhancedDirectMessagesObserver) ifTrue: [self reconnectPrivateChat: each]]! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 14:53'! reconnectChannelChat: model model reconnect! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 15:03'! reconnectPrivateChat: model model class privateMessagesLabel. ! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/21/2002 18:56'! removeChannelObserver: obj self channelObservers remove: obj ifAbsent: [nil]! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 13:56'! serverButton ! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/2/2002 14:56'! subscribedChannelsHavingMemberNamed: aString ^ subscribedChannels select: [:channel | channel hasMember: aString] ! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/26/2002 21:04'! talkTo "talk to some user using 1-to-1 chat messages" | user | user _ FillInTheBlank request: 'user to talk to'. user _ user withBlanksTrimmed. user isEmpty ifTrue: [^ self]. self talkTo: user! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/26/2002 21:06'! talkTo: user user isNil ifTrue: [^ self]. ^IRCEnhancedDirectMessagesObserver openForConnection: self talkingTo: user! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 09:55'! updateButtonNamed: aString newTag: newTag | btn | btns isNil ifTrue: [^ nil]. (btns includesKey: aString) ifFalse: [^ nil]. btn _ btns at: aString. btn label: newTag. "Need to do something about the connect button and we could do that here... " aString = 'connect' ifTrue: [ btn offColor: self offColor. self notifyConfigDialogConnectState]. btn update: nil! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:37'! userNameButton "From dialog" ^ self! ! !IRCConnectionEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 2/26/2002 21:38'! windowIsClosing self quit. self msgObserverShouldClose; channelsBrowserShouldClose; connectDialogShouldClose. btns _ nil. self channelsBrowser: nil. motdPane _ nil! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/20/2002 15:29'! addActionButtonsToWindow: win fractions: fractions offsets: offsets | alignMorph buttons | buttons _ self buttonRow. Preferences alternativeWindowLook ifTrue: [buttons color: Color transparent. buttons submorphsDo: [:m | m borderWidth: 2; borderColor: #raised]]. alignMorph _ AlignmentMorph newRow hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 2; layoutPolicy: ProportionalLayout new. alignMorph addMorph: buttons fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ 0 corner: 0 @ 0)). win addMorph: alignMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [alignMorph borderWidth: 2] ifFalse: [alignMorph borderWidth: 0]! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/25/2002 09:31'! baseWindowTitle ^'Enhanced IRC Console'. ! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 4/4/2002 18:40'! buildCombinedConsoleTextAndInputPane | consolePane inputPane alignMorph inputHeight | inputHeight _ self inputHeight. alignMorph _ AlignmentMorph newColumn hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 0; layoutPolicy: ProportionalLayout new. consolePane _ PluggableTextMorphHybrid on: self text: #consoleText accept: nil readSelection: #consoleTextSelection menu: #consoleMenu:. consolePane name: 'text'. inputPane _ PluggableInterceptingTextMorph on: self text: nil accept: #acceptConsoleString:. inputPane acceptOnCR: true; color: Color white; addIntercept: Character arrowUp; addIntercept: Character arrowDown; name: 'consoleInput'; hideScrollBarIndefinitely. consolePane keysInputPane: inputPane. alignMorph color: IRCClientColors consolePaneBackground; addMorph: consolePane fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ 0 corner: 0 @ inputHeight negated)). alignMorph addMorph: inputPane fullFrame: (LayoutFrame fractions: (0 @ 1 corner: 1 @ 1) offsets: (0 @ inputHeight negated corner: 0 @ 0)). ^ alignMorph! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 3/10/2002 15:38'! buildMorphIn: parentMorph "build views" | buttonHeight consolePane | buttonHeight _ self buttonHeight. self addActionButtonsToWindow: parentMorph fractions: (0 @ 0 corner: 1 @ 0) offsets: (0 @ 0 corner: 0 @ buttonHeight). swapper _ self buildSwapper. parentMorph addMorph: swapper fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ buttonHeight corner: 0 @ 0)). consolePane _ self buildCombinedConsoleTextAndInputPane. self addSwapPane: consolePane named: 'console' hidden: false closeable: false swapModel: self. swapper defaultPaneName: 'console'. swapper fillWithRamp: self paneColorRamp oriented: 6; setMoverButtonColors: Color tan muchLighter. self subscribeToProtocolMessages: self! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/20/2002 22:42'! buttonHeight ^26. ! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/20/2002 21:43'! buttonRow | aRow btn onColor offColor | aRow _ AlignmentMorph newRow beSticky. aRow color: Color transparent. aRow clipSubmorphs: true. aRow layoutInset: 5 @ 1; cellInset: 6. btns _ Dictionary new. self buttonSpecs do: [:spec | btn _ PluggableButtonMorph on: self getState: (spec fourth = #none ifFalse: [spec fourth]) action: spec third. btn color: Color transparent; hResizing: #spaceFill; vResizing: #spaceFill; useRoundedCorners; label: spec first asString. onColor _ spec sixth = #none ifTrue: [Color transparent] ifFalse: [self perform: spec sixth]. offColor _ spec seventh = #none ifTrue: [Color transparent] ifFalse: [self perform: spec seventh]. btn onColor: onColor offColor: offColor. aRow addMorphBack: btn. btn setBalloonText: spec fifth. btns at: spec second put: btn]. ^ aRow! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 3/3/2002 12:11'! buttonSpecs "button label, button name, selector, status, help text, on color, off color" ^ #(#('connect' 'connect' #connectToggle #connected 'Connect/Disconnect IRC server. Blue if you are connected and Red if your connection was dropped.' #onColor #offColor) #('setup' 'setup' #openConnectionDialogue #none 'Open up connection setup dialog' #none #none) #('MOTD' 'motd' #openMotd #none 'Read Message Of The Day' #none #none) #('channels' 'channels' #openChannelList #none 'Open Channels List Window' #none #none) #('pvt msg' 'pvt' #talkTo #none 'Private Message for Individual' #none #none) #('new console' 'newConsole' #newConsole #none 'Open up a new IRC console using same options' #none #none) )! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/28/2002 22:15'! consoleMenu: aMenu aMenu addList: #(#('find (f)' #find) #('find again (g)' #findAgain) #('set search string' #setSearchString) #('copy (c)' #copySelection) ). self frontMostPaneIsCloseable ifTrue: [aMenu addList: #(#('close' #closeTab) )]. ^ aMenu! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/24/2002 12:17'! initialExtent "Oh, so fussy about pane size..." ^ 690 @ 460! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/20/2002 22:41'! inputHeight ^25. ! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/24/2002 00:24'! offColor ^ dropped isNil ifTrue: [Color transparent] ifFalse: [dropped ifTrue: [IRCClientColors connectionDropped] ifFalse: [Color transparent]]! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 2/24/2002 00:24'! onColor ^ IRCClientColors connected! ! !IRCConnectionEnhanced methodsFor: 'pane building' stamp: 'sbw 3/9/2002 22:17'! paneColorRamp ^ {0.0 -> Color paleTan darker . 1.0 -> (Color white alpha: 0.8)}! ! !IRCConnectionEnhanced methodsFor: 'factored swapper' stamp: 'sbw 3/1/2002 22:42'! addSwapPane: aPane named: aString hidden: hidden closeable: closeable swapModel: swapModel swapper addPane: aPane named: aString hidden: hidden closeable: closeable swapModel: swapModel. ! ! !IRCConnectionEnhanced methodsFor: 'factored swapper' stamp: 'sbw 3/1/2002 23:25'! alreadyShowingPrivateMessagesFrom: aNick ^ swapper swappedPanesIncludePaneNamed: aNick! ! !IRCConnectionEnhanced methodsFor: 'factored swapper' stamp: 'sbw 3/1/2002 23:38'! buildSwapper ^ TabbedSwappingControl newSwapControlPaneOrientation: #top showMover: true! ! !IRCConnectionEnhanced methodsFor: 'factored swapper' stamp: 'sbw 3/1/2002 22:58'! closeTab "Action from the close button in the swapper movers pane." swapper closeFrontPane! ! !IRCConnectionEnhanced methodsFor: 'factored swapper' stamp: 'sbw 3/1/2002 22:57'! closeTabs "Close all the closeable tabs." swapper closeAllPanes! ! !IRCConnectionEnhanced methodsFor: 'factored swapper' stamp: 'sbw 3/1/2002 23:20'! frontMost ^swapper frontMost! ! !IRCConnectionEnhanced methodsFor: 'factored swapper' stamp: 'sbw 3/1/2002 23:17'! frontMostPaneIsCloseable ^swapper frontMostPaneIsCloseable! ! !IRCConnectionEnhanced methodsFor: 'factored swapper' stamp: 'sbw 3/1/2002 23:03'! possibleTabStateChange: paneName swapper possibleTabStateChange: paneName! ! !IRCConnectionEnhanced methodsFor: 'msg process' stamp: 'sbw 3/2/2002 15:55'! processIO "do as much network IO as is immediately possible" "do nothing if the socket isn't ready for work" "THIS IS A POLLING LOOP DRIVEN BY MORPHIC'S STEP ENGINE." | amount idx messageText message | self connected ifTrue: [self updateButtonNamed: 'connect' newTag: 'disconnect'] ifFalse: [self updateButtonNamed: 'connect' newTag: 'connect']. (socket isNil or: [socket isValid not or: [socket isConnected not]]) ifTrue: [dropped isNil ifFalse: [dropped ifFalse: [dropped _ true]]. ^ self]. "first do sending" [socket sendDone and: [sendBuffer isNil not or: [protocolMessagesToSend size > 0]]] whileTrue: [sendBuffer ifNil: [protocolMessagesToSend isEmpty ifFalse: [sendBuffer _ protocolMessagesToSend removeFirst asString]]. sendBuffer ifNotNil: ["Diagnostic." false ifTrue: [sendBuffer inspect]. amount _ socket sendSomeData: sendBuffer. sendBuffer _ sendBuffer copyFrom: amount + 1 to: sendBuffer size. sendBuffer isEmpty ifTrue: [sendBuffer _ nil]]]. "now do receiving" [socket dataAvailable] whileTrue: [recieveBuffer _ recieveBuffer , socket getData]. "parse as many messages as possible" [idx _ recieveBuffer indexOf: Character lf. idx > 0] whileTrue: [messageText _ recieveBuffer copyFrom: 1 to: idx. message _ IRCProtocolMessage fromString: messageText. self processMessage: message. recieveBuffer _ recieveBuffer copyFrom: idx + 1 to: recieveBuffer size]! ! !IRCConnectionEnhanced methodsFor: 'msg process' stamp: 'sbw 3/11/2002 01:03'! processPrivmsg: aMessage "handle a PRIVMSG or NOTICE message" "put together the message" | sender recipient text privMessage info | sender _ aMessage prefix. recipient _ aMessage arguments at: 1. text _ aMessage arguments at: 2. privMessage _ IRCMessage sender: sender recipient: recipient text: text. "broadcast the message to all subscribers" info _ subscribedChannels at: recipient asIRCLowercase ifAbsent: []. info ifNotNil: [info subscribers do: [:sub | sub ircMessageRecieved: privMessage]. ^ self]. "Diagnostic" true ifFalse: [ (Dictionary new at: 'time' put: Time primSecondsClock; at: 'aMessage' put: aMessage; at: 'privMessage' put: privMessage; at: 'nick' put: nick; at: 'dSize' put: directMessageSubscribers size; yourself) inspect]. (recipient asIRCLowercase = nick asIRCLowercase or: [sender isNil]) ifTrue: ["We need to do a copy here before we start looping since the collection size can grow while we are processing." directMessageSubscribers copy do: [:sub | sub ircMessageRecieved: privMessage]]! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/9/2002 21:26'! acceptConsoleString: aString "New message path logic." ^ self sendConsoleString: aString from: self! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/3/2002 09:14'! addCrLfAndSendString: aString "Handy for diagnostics. We're taking advantage of the fact that the outgoing message sender converts all objects to strings first. So we can just send a string." | stream | stream _ ReadWriteStream on: ''. stream nextPutAll: aString. stream nextPut: Character cr. stream nextPut: Character lf. ^ self sendMessage: stream contents! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/9/2002 20:20'! convertCmdToProtocolMessage: aString idString: idString | words command rest msg | words _ aString asString substrings. command _ words first. rest _ words size > 1 ifTrue: [aString copyFrom: command size + 2 to: aString size] ifFalse: ['']. msg _ [IRCProtocolMessage command: command arguments: (Array with: idString with: rest asString)] ifError: [:a :b | nil]. ^ msg! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/10/2002 22:13'! privmsgFrom: from to: to text: text "send a private message. 'to' can be a channel name...." | msg | msg _ IRCProtocolMessage prefix: from command: 'privmsg' arguments: (Array with: to with: text). "sbw - The next line is to force behavior as if we received the message we will send. This is because these PRIVMSGs are not echoed back to us." self processMessage: msg. ^ self sendMessage: msg! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/9/2002 20:37'! sendChannelString: aString from: observer channelName: cName "New message logic." ^self sendString: aString from: observer to: cName! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/9/2002 20:51'! sendConsoleString: aString from: observer "New message logic." | result msg | result _ true. aString isEmpty ifFalse: [ msg _ [IRCProtocolMessage fromString: aString asString] ifError: [:a :b | nil]. result _ msg isNil ifTrue: [ self addToConsole: 'ERROR: Could not decode command: "' , aString , '"' , String cr. false] ifFalse: [ msg isLocal ifTrue: [self sendLocalMessage: msg from: observer] ifFalse: [self sendMessage: msg] ]. result ifTrue: [observer textWasSent: aString]]. ^ result! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/9/2002 19:43'! sendLocalMessage: msg from: observer | handler | handler _ self class localMessageHandlers at: msg command ifAbsent: []. handler isNil ifFalse: [ observer perform: handler. ^true ]. ^false! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/9/2002 20:25'! sendMessage: aMessage " is usually an IRCProtocolMessage. However, we may receive a raw string instead if we are processing a diagnostic. It's okay to pass a diagnostic string here since the downstream processor converts all messages it receives into strings before they go out the socket." super sendMessage: aMessage. ^true! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/9/2002 20:37'! sendPrivateString: aString from: observer to: talkingTo "New message logic." ^self sendString: aString from: observer to: talkingTo! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'BJP 4/23/2002 21:10'! sendString: aString from: observer to: idString "New message logic." | result msg | result _ true. aString isEmpty ifFalse: [aString asString linesDo: [:line | result ifTrue: [line isEmpty ifTrue: [line _ ' ']. "Needed to handle blank lines from, e.g., cut 'n' paste." result _ line first = $/ ifTrue: [msg _ self convertCmdToProtocolMessage: line idString: idString. msg isNil ifTrue: [false] ifFalse: [msg isLocal ifTrue: [self sendLocalMessage: msg from: observer] ifFalse: [msg isSpecial ifTrue: [ self privmsgFrom: nil to: idString text: msg specialConvertedForm] ifFalse: [self sendMessage: msg]]]] ifFalse: [self privmsgFrom: nil to: idString text: line]. result ifTrue: [observer textWasSent: line]]]]. ^ result! ! !IRCConnectionEnhanced methodsFor: 'msg process outgoing new' stamp: 'sbw 3/9/2002 11:02'! textWasSent: aText "New message logic." "We are console here." self consoleMessagePlayback addMessage: aText. self addToConsole: 'Command: ' , aText , String cr! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:25'! extractIdleTimeStringFromProtocolMessage: aMessage | stream array idleTime | stream _ ReadWriteStream on: ''. array _ aMessage arguments. idleTime _ Time fromSeconds: (array at: 3) asNumber. stream nextPutAll: (array at: 2); nextPutAll: ' has'. idleTime hours > 0 ifTrue: [stream nextPutAll: ' ' , idleTime hours printString , ' hour'. idleTime hours > 1 ifTrue: [stream nextPut: $s]]. idleTime minutes > 0 ifTrue: [stream nextPutAll: ' ' , idleTime minutes printString , ' minute'. idleTime minutes > 1 ifTrue: [stream nextPut: $s]]. idleTime seconds > 0 ifTrue: [stream nextPutAll: ' ' , idleTime seconds printString , ' second'. idleTime seconds > 1 ifTrue: [stream nextPut: $s]]. stream nextPutAll: ' idle time'. ^ stream contents! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:26'! extractNickIdentifiedStringFromProtocolMessage: aMessage | stream array | stream _ ReadWriteStream on: ''. array _ aMessage arguments. stream nextPutAll: (array at: 3). ^ stream contents! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:27'! extractReplyAwayStringFromProtocolMessage: aMessage | stream array | stream _ ReadWriteStream on: ''. array _ aMessage arguments. stream nextPutAll: (array at: 2); nextPutAll: ' is away: '; nextPutAll: (array at: 3). ^ stream contents! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:28'! extractWhoIsChannelsStringFromProtocolMessage: aMessage | stream array | stream _ ReadWriteStream on: ''. array _ aMessage arguments. stream nextPutAll: 'on channels: '; nextPutAll: (array at: 3). ^ stream contents! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:27'! extractWhoIsServerStringFromProtocolMessage: aMessage | stream array | stream _ ReadWriteStream on: ''. array _ aMessage arguments. stream nextPutAll: 'on irc via server '; nextPutAll: (array at: 3); nextPutAll: ' ('; nextPutAll: (array at: 4); nextPut: $). ^ stream contents! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:27'! extractWhoIsUserStringFromProtocolMessage: aMessage | stream array | stream _ ReadWriteStream on: ''. array _ aMessage arguments. stream nextPutAll: (array at: 2); nextPutAll: ' is '; nextPutAll: (array at: 3); nextPut: $@; nextPutAll: (array at: 4); nextPutAll: ' ('; nextPutAll: (array at: 6); nextPut: $). ^ stream contents! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/9/2002 23:58'! ircMessageRecieved: aMessage | sender newLine | sender _ aMessage sender ifNil: [self nick]. (sender includes: $!!) ifTrue: [sender _ sender copyFrom: 1 to: (sender indexOf: $!!) - 1]. IRCClientSounds genericPrivateMessageArrived. newLine _ (Text string: sender emphasis: (Array with: TextEmphasis bold)) , ': ' , aMessage text , String cr. self addToConsole: newLine. swapper possibleTabStateChange: IRCEnhancedDirectMessagesObserver privateMessagesLabel! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/2/2002 13:00'! processChannelList: aMessage super processChannelList: aMessage. "Let's see if we can try to update the channel list browser." self channelsBrowser isNil ifFalse: [self channelsBrowser channelListDownloading: channelListBeingBuilt size]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 2/22/2002 10:39'! processChannelListEnd: aMessage super processChannelListEnd: aMessage. self channelsBrowser isNil ifFalse: [self channelsBrowser channelListDownloadCompleted]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/2/2002 13:11'! processChannelsServerLoadTooHigh: aMessage super processChannelListEnd: nil. self channelsBrowser isNil ifFalse: [self channelsBrowser channelListDownloadAborted]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 2/25/2002 21:43'! processEndOfWhoIs: aMessage ! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:29'! processIdleTime: aMessage | string | string _ self extractIdleTimeStringFromProtocolMessage: aMessage. self sendString: string withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: nil! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/9/2002 22:28'! processInvite: aMessage "a user is inviting us to join a channel" | channelName user observer | (self option: #acceptChannelInvites) ifFalse: [^ nil]. channelName _ aMessage arguments second. user _ aMessage prefix. (user includes: $!!) ifTrue: [user _ user copyFrom: 1 to: (user indexOf: $!!) - 1]. observer _ IRCChannelObserverEnhanced openForChannelNamed: channelName onCaptiveConnection: self. (self channelInfo: channelName) addMember: self nick. self addChannelObserver: observer. observer addTextToChannelPane: user , ' has invited you to this channel.' , Character cr asString usingUnpreparedNick: self serverPrefixMessageNick; updateUsers! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:30'! processJoin: aMessage "a user is joining a channel" | channelName user textMessage | channelName _ aMessage arguments first. user _ aMessage prefix. (user includes: $!!) ifTrue: [user _ user copyFrom: 1 to: (user indexOf: $!!) - 1]. (self channelInfo: channelName) addMember: user. (self channelObservers select: [:each | each channelName = channelName]) do: [:obs | obs updateUsers]. user = self nick ifTrue: [^ nil]. (self option: #showJoinsInChannel) ifTrue: [textMe