'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: [textMessage _ user , ' has joined the channel.'. self sendString: textMessage withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: channelName]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/10/2002 20:38'! processMessage: aMessage false ifTrue: [aMessage inspect]. super processMessage: aMessage! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/10/2002 22:16'! processMode: aMessage | channelName memberName modeVerb modeChange textMessage user | channelName _ aMessage arguments at: 1. modeChange _ aMessage arguments at: 2. ('vo' includes: modeChange second) ifFalse: [^ nil]. memberName _ aMessage arguments at: 3. user _ aMessage prefix. (user includes: $!!) ifTrue: [user _ user copyFrom: 1 to: (user indexOf: $!!) - 1]. modeVerb _ 'modified status for'. modeChange = '+v' ifTrue: [modeVerb _ 'voiced']. modeChange = '-v' ifTrue: [modeVerb _ 'un-voiced']. modeChange = '+o' ifTrue: [modeVerb _ 'opped']. modeChange = '-o' ifTrue: [modeVerb _ 'de-opped']. (self channelInfo: channelName) modeChanged: modeChange forMemberNamed: memberName. (self channelObservers select: [:each | each channelName asIRCLowercase = channelName asIRCLowercase]) do: [:obs | obs updateUsers]. (self option: #showModeChangesInChannel) ifTrue: [textMessage _ user , ' has ' , modeVerb , ' ' , memberName , '.'. self sendString: textMessage withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: channelName]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/1/2002 11:14'! processNamReply: aMessage "list of names for a channel, probably a channel being joined" | names lChannelName channelInfo channelMembers channelMember | lChannelName _ (aMessage arguments at: 3) asIRCLowercase. channelMembers _ OrderedCollection new. names _ aMessage arguments last findTokens: ' '. names do: [:origName | channelMember _ IRCChannelMember fromString: origName. channelMember channel: lChannelName. channelMembers add: channelMember]. channelInfo _ subscribedChannels at: lChannelName ifAbsent: []. channelInfo ifNotNil: [channelInfo addMembers: channelMembers]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/7/2002 02:59'! processNickChanged: aMessage | newNick user channelName textMessage | newNick _ aMessage arguments first. user _ aMessage prefix. (user includes: $!!) ifTrue: [user _ user copyFrom: 1 to: (user indexOf: $!!) - 1]. (self subscribedChannelsHavingMemberNamed: user) do: [:chanInfo | chanInfo nickFor: user changedTo: newNick. channelName _ chanInfo name. (self channelObservers select: [:each | each channelName = channelName]) do: [:obs | obs updateUsers. textMessage _ 'User ' , user , ' has changed nick to: ' , newNick. self sendString: textMessage withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: channelName]]. user = nick ifTrue: [self nick: newNick]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:31'! processNickIdentified: aMessage | string | string _ self extractNickIdentifiedStringFromProtocolMessage: aMessage. self sendString: string withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: nil! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:31'! processPart: aMessage "a user is leaving 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) removeMember: user. (self channelObservers select: [:each | each channelName = channelName]) do: [:obs | obs updateUsers]. (self option: #showPartsInChannel) ifTrue: [textMessage _ user , ' has left the channel. '. aMessage arguments size > 1 ifTrue: [textMessage _ textMessage , (aMessage arguments at: 2)]. self sendString: textMessage withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: channelName]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:31'! processQuit: aMessage "a user has left IRC. Remove them from all chanels" | user textMessage | user _ aMessage prefix. (user includes: $!!) ifTrue: [user _ user copyFrom: 1 to: (user indexOf: $!!) - 1]. subscribedChannels do: [:channel | (channel hasMember: user) ifTrue: [channel removeMember: user]]. self channelObservers do: [:obs | obs updateUsers]. (self option: #showQuitsInChannel) ifTrue: [textMessage _ user , ' has quit IRC. ' , (aMessage arguments at: 1). self sendString: textMessage withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: nil]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:32'! processReplyAway: aMessage | string | string _ self extractReplyAwayStringFromProtocolMessage: aMessage. self sendString: string withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: nil! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:32'! processReplyWhoIsServer: aMessage | string | string _ self extractWhoIsServerStringFromProtocolMessage: aMessage. self sendString: string withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: nil! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:32'! processReplyWhoIsUser: aMessage | string | string _ self extractWhoIsUserStringFromProtocolMessage: aMessage. self sendString: string withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: nil! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:32'! processWhoIsChannels: aMessage | string | string _ self extractWhoIsChannelsStringFromProtocolMessage: aMessage. self sendString: string withNickTag: self serverPrefixMessageNick ifFrontChannelObserverIs: nil! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/10/2002 22:16'! sendString: aString withNickTag: nickTag ifFrontChannelObserverIs: channelName "Utility method. If you don't know the channelName just use nil." | frontModel | self frontMost isNil ifTrue: [^ nil]. self frontMost externalName = 'console' ifTrue: [^ nil]. channelName isNil ifFalse: [self frontMost externalName asIRCLowercase = channelName asIRCLowercase ifFalse: [^ nil]]. (self frontMost hasProperty: #swapModel) ifFalse: [^ nil]. frontModel _ self frontMost valueOfProperty: #swapModel. (frontModel isKindOf: IRCChannelObserverEnhanced) ifTrue: [frontModel addTextToChannelPane: aString , Character cr asString usingUnpreparedNick: nickTag]. (frontModel isKindOf: IRCEnhancedDirectMessagesObserver) ifTrue: [frontModel addToChatText: aString , Character cr asString]! ! !IRCConnectionEnhanced methodsFor: 'msg process incoming' stamp: 'sbw 3/5/2002 16:23'! serverPrefixMessageNick "This is the prefix we use when posting messages from the server." ^'***'! ! !IRCConnectionEnhanced methodsFor: 'keys intercept management' stamp: 'sbw 2/25/2002 14:35'! checkForScrollPlaybackDownOn: morph | previous | self consoleMessagePlayback rollIndexForward. previous _ self consoleMessagePlayback getIndexedMessage. previous isNil ifFalse: [morph setText: previous. morph setSelection: (previous size + 1 to: previous size + 1). morph hasUnacceptedEdits: true]. ^ false! ! !IRCConnectionEnhanced methodsFor: 'keys intercept management' stamp: 'sbw 2/25/2002 14:35'! checkForScrollPlaybackUpOn: morph | previous | previous _ self consoleMessagePlayback getIndexedMessage. self consoleMessagePlayback rollIndexBack. previous isNil ifFalse: [morph setText: previous. morph setSelection: (previous size + 1 to: previous size + 1). morph hasUnacceptedEdits: true]. ^ false! ! !IRCConnectionEnhanced methodsFor: 'keys intercept management' stamp: 'sbw 2/25/2002 14:35'! keyInterceptEndedFrom: morph self consoleMessagePlayback resetIndex! ! !IRCConnectionEnhanced methodsFor: 'keys intercept management' stamp: 'sbw 2/25/2002 14:36'! 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. IRCConnectionEnhanced (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 arrowUp ifTrue: [^ self checkForScrollPlaybackUpOn: morph]. char = Character arrowDown ifTrue: [^ self checkForScrollPlaybackDownOn: morph]. self keyInterceptEndedFrom: morph. ^ true! ! !IRCConnectionEnhanced methodsFor: 'options management' stamp: 'sbw 3/3/2002 10:05'! option: key ^ self options at: key ifAbsent: [false]! ! !IRCConnectionEnhanced methodsFor: 'options management' stamp: 'sbw 3/3/2002 10:06'! option: key value: value "Set the option value." ^ self options at: key put: value! ! !IRCConnectionEnhanced methodsFor: 'options management' stamp: 'sbw 3/3/2002 10:05'! options options == nil ifTrue: [self populateOptionsFrom: self populateOptionsFromDefaults]. ^ options! ! !IRCConnectionEnhanced methodsFor: 'options management' stamp: 'sbw 3/3/2002 12:14'! populateOptionsFrom: dict options _ dict copy! ! !IRCConnectionEnhanced methodsFor: 'options management' stamp: 'sbw 3/3/2002 12:14'! populateOptionsFromDefaults ^ self class savedOptions! ! !IRCConnectionEnhanced methodsFor: 'options management' stamp: 'sbw 3/3/2002 11:27'! toggleOption: aSymbol ^self option: aSymbol value: (self option: aSymbol) not! ! !IRCConnectionEnhanced class methodsFor: 'release notes - edit strings' stamp: 'sbw 3/10/2002 15:43'! featuresString "IRCConnectionEnhanced features" "IRCConnectionEnhanced installNewPreambleForIRCChangeSet" ^ '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. '! ! !IRCConnectionEnhanced class methodsFor: 'release notes - edit strings' stamp: 'sbw 4/20/2003 07:53'! revisionsString "IRCConnectionEnhanced revisions" "IRCConnectionEnhanced installNewPreambleForIRCChangeSet" ^ '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 '! ! !IRCConnectionEnhanced class methodsFor: 'release notes - edit strings' stamp: 'sbw 11/21/2002 19:31'! toDoString "IRCConnectionEnhanced toDoList" "IRCConnectionEnhanced installNewPreambleForIRCChangeSet" ^ '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 '! ! !IRCConnectionEnhanced class methodsFor: 'release notes - publish tools' stamp: 'sbw 3/5/2002 10:09'! buildReleaseNotesString ^ self featuresString , self revisionsString , self toDoString! ! !IRCConnectionEnhanced class methodsFor: 'release notes - publish tools' stamp: 'sbw 3/5/2002 11:16'! buildReleaseNotesStringForChangeSetPreamble "IRCConnectionEnhanced buildReleaseNotesStringForChangeSetPreamble" "Create a string that can be pasted into the body of the preamble for a change set. We will have a new string that does not include double ' for embedded single quotes and DOES include double quote characters properly handled." | sourceString stream | sourceString _ self buildReleaseNotesString. stream _ ReadWriteStream on: ''. sourceString do: [:char | stream nextPut: char. char = $" ifTrue: [stream nextPut: char]]. ^ stream contents! ! !IRCConnectionEnhanced class methodsFor: 'release notes - publish tools' stamp: 'sbw 3/5/2002 11:12'! features "IRCConnectionEnhanced features" self featuresString openInWorkspaceWithTitle: 'General Notes and Features'! ! !IRCConnectionEnhanced class methodsFor: 'release notes - publish tools' stamp: 'sbw 3/5/2002 11:46'! installNewPreambleForChangeSetNamed: aString | changeSet stream line originalPreamble | changeSet _ ChangeSorter changeSetNamed: aString. changeSet isNil ifTrue: [^ self inform: 'Change set "' , aString , '" not found.']. "Keep the first 4 lines from the original preamble text." originalPreamble _ changeSet preambleString. stream _ ReadWriteStream on: ''. 1 to: 4 do: [:index | line _ originalPreamble lineNumber: index. line isEmpty ifFalse: [stream nextPutAll: line]. stream cr]. "Add the new body of text." stream nextPutAll: self buildReleaseNotesStringForChangeSetPreamble. "Close with a double quote." stream nextPut: $". changeSet preambleString: stream contents! ! !IRCConnectionEnhanced class methodsFor: 'release notes - publish tools' stamp: 'sbw 3/11/2002 20:50'! installNewPreambleForIRCChangeSet " DO THIS WHEN YOU EDIT RELEASE NOTES. This method will insert the proper change set preamble to the change set named 'IRCEnhancements-sbw'. " "IRCConnectionEnhanced installNewPreambleForIRCChangeSet" self installNewPreambleForChangeSetNamed: 'IRCEnhancements-sbw'! ! !IRCConnectionEnhanced class methodsFor: 'release notes - publish tools' stamp: 'sbw 3/5/2002 11:13'! releaseNotes "IRCConnectionEnhanced releaseNotes" self buildReleaseNotesString openInWorkspaceWithTitle: 'IRC Enhancements Release Notes'! ! !IRCConnectionEnhanced class methodsFor: 'release notes - publish tools' stamp: 'sbw 3/5/2002 11:13'! revisions "IRCConnectionEnhanced revisions" self revisionsString openInWorkspaceWithTitle: 'Revisions'! ! !IRCConnectionEnhanced class methodsFor: 'release notes - publish tools' stamp: 'sbw 3/5/2002 11:13'! toDoList "IRCConnectionEnhanced toDoList" self toDoString openInWorkspaceWithTitle: 'TO DO list: IRC Enhancements'! ! !IRCConnectionEnhanced class methodsFor: 'as yet unclassified' stamp: 'sbw 11/17/2002 18:40'! initialize "IRCConnectionEnhanced initialize" super initialize. AllOptionKeys _ nil. self initializeLocalMessageHandlers. self initializeOutgoingMessageHandlers. self initializeServers. self addDefaultProfile. self fixAllBadProfileKeys! ! !IRCConnectionEnhanced class methodsFor: 'as yet unclassified' stamp: 'sbw 1/11/2003 18:39'! postLoadOperation "IRCConnectionEnhanced postLoadOperation" "Do this in the right order please..." IRCConnectionEnhanced initialize. IRCServer populateServersDatabaseWithBuiltIn. IRCConnectionEnhanced removeServerNamed: 'Open Projects Net' address: 'irc.openprojects.net'! ! !IRCConnectionEnhanced class methodsFor: 'as yet unclassified' stamp: 'sbw 11/21/2002 22:00'! registerInOpenMenu super registerInOpenMenu. TheWorldMenu registerOpenCommand: {'Enhanced IRC chat'. {IRCConnectionEnhanced. #openConnectionView}. 'Enhanced Internet Relay Chat'}! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 5/19/1999 08:53'! addServer: anIRCServer ServersList isNil ifTrue: [self initializeServers]. ServersList add: anIRCServer! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 2/24/2002 21:05'! buildServersListFrommIRCIniFilePath: aString "Populate the available servers class var using an mIRC servers.ini file for food. The file is found via ." | fileStream | fileStream _ FileStream readOnlyFileNamed: aString. fileStream isNil ifTrue: [^ nil]. self buildServersListFrommIRCIniStream: fileStream. ! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 2/24/2002 21:04'! buildServersListFrommIRCIniStream: aStream | line candidate | [line _ aStream nextLine withBlanksTrimmed. aStream atEnd] whileFalse: [candidate _ IRCServer from: line. candidate isNil ifFalse: [self addServer: candidate]]. ! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 5/19/1999 08:58'! initializeServers "IRCConnectionEnhanced initializeServers" ServersList _ OrderedCollection new! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 5/20/1999 11:01'! noGroupTag ^''! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 1/11/2003 18:36'! removeServer: anIRCServer ServersList isNil ifFalse: [ServersList remove: anIRCServer ifAbsent: []]! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 1/11/2003 18:38'! removeServerNamed: aString address: addString | found | found _ ServersList detect: [:each | each nameToUseForProfile = aString and: [each address = addString]] ifNone: []. found isNil ifFalse: [self removeServer: found]! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 3/3/2002 11:02'! serverWithAddress: addrString "Answers the instance of IRCServer from the ServersList having an address . Answers nil if not found." | index | index _ self serversList findFirst: [:each | each address = addrString]. index = 0 ifTrue: [^nil]. ^ self serversList at: index! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 2/29/2000 15:43'! serversList "IRCConnectionEnhanced serversList" ^ServersList! ! !IRCConnectionEnhanced class methodsFor: 'servers list' stamp: 'sbw 2/29/2000 15:49'! serversListByGroups "IRCConnectionEnhanced serversListByGroups" | dict key undefined | undefined _ IRCServer undefinedGroupName. dict _ Dictionary new. self serversList do: [:entry | key _ entry group withBlanksTrimmed. key isEmpty ifTrue: [key _ undefined]. (dict includesKey: key) ifFalse: [dict at: key put: OrderedCollection new]. (dict at: key) add: entry]. ^dict! ! !IRCConnectionEnhanced class methodsFor: 'options' stamp: 'sbw 1/11/2003 13:34'! allOptionKeys | dict | dict _ Dictionary new. self populateOptions: dict. AllOptionKeys _ dict keys. ^ AllOptionKeys! ! !IRCConnectionEnhanced class methodsFor: 'options' stamp: 'sbw 11/17/2002 18:46'! checkForMissingOptionsIn: dict self allOptionKeys do: [:key | (dict includesKey: key) ifFalse: [dict at: key put: false]]! ! !IRCConnectionEnhanced class methodsFor: 'options' stamp: 'sbw 1/11/2003 13:35'! populateOptions: dict "This is where we define options. If you add or delete an option key, perform: IRCConnectionEnhanced savedOptions" dict at: #acceptChannelInvites put: true; at: #privateMessageOpensNewWindow put: true; at: #showJoinsInChannel put: true; at: #showPartsInChannel put: true; at: #showQuitsInChannel put: true; at: #showModeChangesInChannel put: true; at: #usemIRCColors put: false; at: #logChannelTraffic put: false! ! !IRCConnectionEnhanced class methodsFor: 'options' stamp: 'sbw 11/17/2002 18:43'! savedOptions "IRCConnectionEnhanced savedOptions" SavedOptions == nil ifTrue: [SavedOptions _ Dictionary new. self populateOptions: SavedOptions] ifFalse: [self checkForMissingOptionsIn: SavedOptions]. ^ SavedOptions! ! !IRCConnectionEnhanced class methodsFor: 'message handling support' stamp: 'sbw 3/9/2002 19:45'! initializeLocalMessageHandlers "initilize the table mapping local commands to processing methods" "IRCConnectionEnhanced initializeLocalMessageHandlers" "Keys in the dictionary should all be lowercase for later matching." LocalMessageHandlers _ Dictionary new. #(#clear #clearTextPane #inspect #inspect #explore #explore ) pairsDo: [:command :method | LocalMessageHandlers at: command asString put: method]! ! !IRCConnectionEnhanced class methodsFor: 'message handling support' stamp: 'sbw 3/2/2002 14:45'! initializeMessageHandlers "initilize the table mapping IRC commands to processing methods" "IRCConnectionEnhanced initializeMessageHandlers" MessageHandlers _ Dictionary new. #(#ping #processPing: #join #processJoin: #nick #processNickChanged: #part #processPart: #quit #processQuit: #privmsg #processPrivmsg: #notice #processPrivmsg: #topic #processTopic: #invite #processInvite: #mode #processMode: 263 #processChannelsServerLoadTooHigh: 301 #processReplyAway: 307 #processNickIdentified: 311 #processReplyWhoIsUser: 312 #processReplyWhoIsServer: 317 #processIdleTime: 318 #processEndOfWhoIs: 319 #processWhoIsChannels: 321 #processChannelListStart: 322 #processChannelList: 323 #processChannelListEnd: 331 #processNoTopic: 332 #processTopic: 353 #processNamReply: 372 #processMotd: 377 #processMotd: 375 #processMotdStart: 376 #processMotdEnd: ) pairsDo: [:command :method | MessageHandlers at: command asString put: method]! ! !IRCConnectionEnhanced class methodsFor: 'message handling support' stamp: 'sbw 3/2/2002 16:19'! initializeOutgoingMessageHandlers "initilize the table mapping /commands to processing methods. Note that these conversion are all performed as IRCProtocolMessage instance methods." "IRCConnectionEnhanced initializeOutgoingMessageHandlers" "Keys in the dictionary should all be lowercase for later matching." OutgoingMessageHandlers _ Dictionary new. #(#whois #processWhoisCommand #nick #processNickCommand #join #processJoinCommand #part #processPartCommand ) pairsDo: [:command :method | OutgoingMessageHandlers at: command asString put: method]! ! !IRCConnectionEnhanced class methodsFor: 'message handling support' stamp: 'sbw 3/2/2002 09:46'! localMessageHandlers LocalMessageHandlers isNil ifTrue: [self initializeLocalMessageHandlers]. ^LocalMessageHandlers! ! !IRCConnectionEnhanced class methodsFor: 'message handling support' stamp: 'sbw 3/2/2002 16:08'! outgoingMessageHandlers OutgoingMessageHandlers isNil ifTrue: [self initializeOutgoingMessageHandlers]. ^ OutgoingMessageHandlers! ! !IRCConnectionEnhanced class methodsFor: 'message handling support' stamp: 'sbw 3/9/2002 20:10'! specialCommands "Answers the list of special commands that require some additional translation before they are sent." "See http://www.irchelp.org/irchelp/rfc/ctcpspec.html" ^ #('action' 'dcc' 'sed' )! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/3/2002 16:42'! addDefaultProfile "IRCConnectionEnhanced addDefaultProfile" "Replace existing one if already there." self profiles at: IRCConnectionProfile default name put: IRCConnectionProfile default ! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/3/2002 11:19'! addProfile: anIRCProfile (self profiles includesKey: anIRCProfile name) ifTrue: [^ self inform: 'Profile "' , anIRCProfile name , '" already defined.']. self profiles at: anIRCProfile name put: anIRCProfile! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/5/2002 00:35'! checkKeyForProfileNamed: aString "Ensure that the key we are using is the same the profile name. Assume that the profile name is the correct one if they differ." | badKeys profile | (self profiles keys includes: aString) ifFalse: [ badKeys _ Set new. self profiles keys do: [:ky | profile _ self profiles at: ky. profile name = aString ifTrue: [ ky = profile name ifFalse: [ self profiles at: aString put: profile copy. badKeys add: ky. ] ] ]. badKeys do: [:badKey | self profiles removeKey: badKey] ]! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/3/2002 23:57'! defaultFullName ^ DefaultFullName! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/3/2002 23:56'! defaultNick ^ DefaultNick! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/3/2002 23:57'! defaultUserName ^ DefaultUserName! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/5/2002 00:39'! fixAllBadProfileKeys "Ensure that the keys we are using are the same their profile name. Assume that the profile name is the correct one if they differ." "IRCConnectionEnhanced fixAllBadProfileKeys" "IRCConnectionEnhanced profiles inspect" (self profiles collect: [:each | each name]) do: [:pName | self checkKeyForProfileNamed: pName]! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/4/2002 10:59'! getProfileNamed: aString (self profiles includesKey: aString) ifFalse: [^ nil]. ^self profiles at: aString! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/4/2002 07:46'! hasProfileNamed: aString ^self profiles includesKey: aString! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/3/2002 11:18'! initializeProfiles "IRCConnectionEnhanced initializeProfiles" Profiles _ Dictionary new! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/3/2002 11:21'! profiles "IRCConnectionEnhanced profiles" Profiles == nil ifTrue: [self initializeProfiles]. ^ Profiles! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/4/2002 01:07'! removeProfile: anIRCProfile self removeProfileNamed: anIRCProfile name! ! !IRCConnectionEnhanced class methodsFor: 'profiles' stamp: 'sbw 3/4/2002 01:07'! removeProfileNamed: aString (self profiles includesKey: aString) ifFalse: [^ self inform: 'Profile "' , aString , '" does not exist.']. self profiles removeKey: aString! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 23:58'! fullName fullName == nil ifTrue: [nick _ IRCConnectionEnhanced defaultFullName]. ^ fullName! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 23:58'! fullName: str fullName _ str! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:23'! name name == nil ifTrue: [name _ 'unnamed']. ^name! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:22'! name: aString "The name of thiss profile." name _ aString! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:25'! nick nick == nil ifTrue: [nick _ IRCConnectionEnhanced defaultNick]. ^nick! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:23'! nick: aString "The nick used with this profile." nick _ aString! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 11/17/2002 18:44'! options options == nil ifTrue: [options _ IRCConnectionEnhanced savedOptions] ifFalse: [IRCConnectionEnhanced checkForMissingOptionsIn: options]. ^ options! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 11:09'! options: dict options _ dict! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:28'! server server = nil ifTrue: [server _ IRCServer defaultServer]. ^server! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:28'! server: anIRCServer server _ anIRCServer! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 23:59'! userName userName == nil ifTrue: [nick _ IRCConnectionEnhanced defaultUserName]. ^ userName! ! !IRCConnectionProfile methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 23:59'! userName: str userName _ str! ! !IRCConnectionProfile class methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:52'! default | model | model _ self fromConnection: IRCConnectionEnhanced new. model name: self defaultProfileName. ^model! ! !IRCConnectionProfile class methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 10:51'! defaultProfileName ^'Default Profile'! ! !IRCConnectionProfile class methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 00:00'! fromConnection: anIRCConnectionEnhanced | model server | model _ self new. server _ IRCConnectionEnhanced serverWithAddress: anIRCConnectionEnhanced server. server isNil ifTrue: [server _ IRCServer defaultServer] ifFalse: [server port: anIRCConnectionEnhanced port]. model nick: anIRCConnectionEnhanced nick; fullName: anIRCConnectionEnhanced fullName; userName: anIRCConnectionEnhanced userName; server: server; options: anIRCConnectionEnhanced options. ^ model! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 23:29'! aboutToBeClosed talkingTo isNil ifTrue: [connection msgObserver: nil. self release]. ^ true! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 11:47'! 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! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 11:47'! 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! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 2/28/2002 21:30'! closeFrontMostWindow connection closeTab! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 16:42'! initialExtent ^ 450 @ 160! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 11:47'! keyInterceptEndedFrom: morph self messagePlayback resetIndex! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 11:55'! 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. IRCEnhancedDirectMessagesObserver (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 arrowUp ifTrue: [^ self checkForScrollPlaybackUpOn: morph]. char = Character arrowDown ifTrue: [^ self checkForScrollPlaybackDownOn: morph]. self keyInterceptEndedFrom: morph. ^ true! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 11:46'! messagePlayback messagePlayback == nil ifTrue: [messagePlayback _ IRCMessagePlayback new]. ^ messagePlayback! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 12:40'! openMorphicView "We do it inside the console window!!" "open a view for messages sent here" | paneName alignMorph | paneName _ talkingTo ifNil: [self class privateMessagesLabel] ifNotNil: [talkingTo]. 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: talkingTo isNil closeable: talkingTo isNil not swapModel: self. ! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'as yet unclassified' stamp: 'sbw 2/22/2002 17:06'! perform: aSelector orSendTo: rcvr (self respondsTo: aSelector) ifTrue: [^ self perform: aSelector]. ^ rcvr perform: aSelector! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'pane building' stamp: 'sbw 4/4/2002 18:40'! buildMorphIn: parentMorph | textArea inputHeight inputArea | textArea _ PluggableTextMorphHybrid on: self text: #chatText accept: nil readSelection: #chatTextSelection menu: #channelTextMenu:. inputHeight _ talkingTo isNil ifTrue: [0] ifFalse: [26]. parentMorph addMorph: textArea fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ 0 corner: 0 @ inputHeight negated)). talkingTo isNil ifFalse: [inputArea _ PluggableInterceptingTextMorph on: self text: nil accept: #acceptPrivateString:. inputArea acceptOnCR: true; addIntercept: Character arrowUp; addIntercept: Character arrowDown; hideScrollBarIndefinitely. parentMorph addMorph: inputArea fullFrame: (LayoutFrame fractions: (0 @ 1 corner: 1 @ 1) offsets: (0 @ inputHeight negated corner: 0 @ 0)). textArea keysInputPane: inputArea]! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'pane building' stamp: 'sbw 3/10/2002 00:07'! channelTextMenu: aMenu aMenu addList: #(#('find' #find) #('find again' #findAgain) #('set search string' #setSearchString) #('copy' #copySelection) ). connection frontMostPaneIsCloseable ifTrue: [aMenu addList: #(#('close' #closeFrontMostWindow) )]. ^ aMenu! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'text pane' stamp: 'sbw 3/2/2002 11:08'! clearTextPane chatText _ Text new. self changed: #chatText! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'messages' stamp: 'sbw 3/9/2002 21:27'! acceptPrivateString: aString "New send message logic." talkingTo isNil ifTrue: [^ true]. ^ connection sendPrivateString: aString from: self to: talkingTo! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'messages' stamp: 'sbw 3/10/2002 00:22'! addWithNickToChatText: aText "new message logic." | newLine | newLine _ (Text string: connection nick attribute: TextEmphasis bold) , ': ' , aText , String cr. self addToChatText: newLine! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'messages' stamp: 'sbw 5/5/2002 09:54'! ircMessageRecieved: aMessage | sender newLine nickText pvt messageText messageWords actionFlag | sender _ aMessage sender ifNil: ["connection nick" 'Server']. (sender includes: $!!) ifTrue: [sender _ sender copyFrom: 1 to: (sender indexOf: $!!) - 1]. "Check see if someone else is handling this already for us if we are the generic private messages handler." talkingTo isNil ifTrue: [(connection alreadyShowingPrivateMessagesFrom: sender) ifTrue: [^ nil]]. "See if we should open a new window and let that one have this message." (talkingTo isNil and: [aMessage sender isNil not]) ifTrue: [(connection option: #privateMessageOpensNewWindow) ifTrue: [pvt _ connection talkTo: sender. ^ pvt ircMessageRecieved: aMessage]]. talkingTo ifNotNil: [aMessage sender isNil ifFalse: ["only display messages to or from talkingTo" sender asIRCLowercase = talkingTo asIRCLowercase ifFalse: [^ self]]]. (talkingTo isNil and: [aMessage sender isNil]) ifTrue: [^ self]. "Check if we should be posting our own message here." aMessage sender isNil ifTrue: [ aMessage recipient = talkingTo ifFalse: [^nil]]. aMessage sender isNil ifTrue: [sender _ connection nick]. nickText _ Text string: sender emphasis: (Array with: TextEmphasis bold). 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. aMessage sender isNil ifTrue: ["message is going out" newLine addAttribute: IRCClientColors transmittedMessage]. aMessage sender isNil ifFalse: [IRCClientSounds specificPrivateMessageArrived]. actionFlag ifTrue: [newLine addAttribute: TextEmphasis italic]. self addToChatText: newLine. talkingTo isNil ifTrue: [connection possibleTabStateChange: self class privateMessagesLabel] ifFalse: [connection possibleTabStateChange: sender]! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'messages' stamp: 'sbw 3/10/2002 14:26'! talkingTo ^talkingTo! ! !IRCEnhancedDirectMessagesObserver methodsFor: 'messages' stamp: 'sbw 3/10/2002 21:10'! textWasSent: aText "new message logic." self messagePlayback addMessage: aText. true ifFalse: ["Bypass this for now..." self addWithNickToChatText: aText]! ! !IRCEnhancedDirectMessagesObserver class methodsFor: 'as yet unclassified' stamp: 'sbw 2/21/2002 22:03'! privateMessagesLabel ^'private messages'! ! !IRCMessagePlayback methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 09:56'! addMessage: aString self playBack add: aString. self playBack size > self class rollbackLimit ifTrue: [self playBack removeFirst]! ! !IRCMessagePlayback methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 10:24'! getIndexedMessage self playBack isEmpty ifTrue: [^ nil]. self index == nil ifTrue: [self index: self playBack size]. ^self playBack at: self index. ! ! !IRCMessagePlayback methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 09:52'! index ^index! ! !IRCMessagePlayback methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 09:52'! index: num index _ num! ! !IRCMessagePlayback methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 09:49'! playBack playBack == nil ifTrue: [playBack _ OrderedCollection new]. ^ playBack! ! !IRCMessagePlayback methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 10:29'! resetIndex self index: nil! ! !IRCMessagePlayback methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 10:23'! rollIndexBack self index == nil ifTrue: [^nil]. self index <= 1 ifTrue: [^nil]. self index: self index - 1! ! !IRCMessagePlayback methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 10:24'! rollIndexForward self index == nil ifTrue: [^ nil]. self index = self playBack size ifTrue: [^nil]. self index: self index + 1! ! !IRCMessagePlayback class methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 09:49'! rollbackLimit RollbackLimit == nil ifTrue: [RollbackLimit _ 40]. ^RollbackLimit! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 15:43'! addActionButtonsTo: parentMorph fractions: fractions offsets: offsets | pane | pane _ self buildActionButtons. parentMorph addMorph: pane fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [pane borderWidth: 2] ifFalse: [pane borderWidth: 0]! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 15:42'! addFullNamePaneTo: parentMorph fractions: fractions offsets: offsets | pane | pane _ self buildFullNamePane. parentMorph addMorph: pane fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [pane borderWidth: 2] ifFalse: [pane borderWidth: 0]! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 15:41'! addNickPaneTo: parentMorph fractions: fractions offsets: offsets | pane | pane _ self buildNickPane. parentMorph addMorph: pane fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [pane borderWidth: 2] ifFalse: [pane borderWidth: 0]! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 15:40'! addProfileNamePaneTo: parentMorph fractions: fractions offsets: offsets | pane | pane _ self buildProfileNamePane. parentMorph addMorph: pane fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [pane borderWidth: 2] ifFalse: [pane borderWidth: 0]! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 15:43'! addServerAddressPaneTo: parentMorph fractions: fractions offsets: offsets | pane | pane _ self buildServerAddressPane. parentMorph addMorph: pane fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [pane borderWidth: 2] ifFalse: [pane borderWidth: 0]! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 15:42'! addUserNamePaneTo: parentMorph fractions: fractions offsets: offsets | pane | pane _ self buildUserNamePane. parentMorph addMorph: pane fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [pane borderWidth: 2] ifFalse: [pane borderWidth: 0]! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 16:09'! buildDialogOn: window | coll frac off count deltaY | coll _ OrderedCollection new. coll add: #addProfileNamePaneTo:fractions:offsets:; add: #addNickPaneTo:fractions:offsets:; add: #addFullNamePaneTo:fractions:offsets:; add: #addUserNamePaneTo:fractions:offsets:; add: #addServerAddressPaneTo:fractions:offsets:; add: #addActionButtonsTo:fractions:offsets:. count _ coll size. deltaY _ (1 / count) asFloat. 1 to: count - 1 do: [:index | frac _ 0 @ (index - 1 * deltaY) corner: 1 @ (index * deltaY). off _ 0 @ 0 corner: 0 @ 0. self perform: (coll at: index) with: window with: frac with: off]. self perform: coll last with: window with: (0 @ (count - 1 * deltaY) corner: 1 @ 1) with: (0 @ 0 corner: 0 @ 0)! ]style[(15 6 4 27 4 4 3 17 7 4 6 40 10 33 10 37 10 37 10 42 10 38 3 5 3 4 8 6 4 1 3 5 12 1 7 5 3 1 8 7 6 4 3 1 4 5 3 1 3 6 10 1 4 5 3 6 6 3 3 1 3 1 9 1 3 1 5 4 15 4 5 5 12 6 11 4 11 3 4 4 12 4 30 1 4 5 3 1 3 6 10 1 3 1 11 1 3 1 9 1 3 1 1)f1b,f1cblue;b,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1c024176000,f1,f1cred;,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1c024176000,f1,f1cblue;i,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1,f1c024176000,f1! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 23:08'! editRowHeight ^24! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 12:22'! initialExtent ^ 560 @ 200! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 00:09'! labelRowWidth ^ 120! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 22:43'! openDialog "private" "Get here after we do the server dialog window." | dialogue | dialogue _ SystemWindow new. dialogue model: self. self buildDialogOn: dialogue. dialogue setLabel: 'add a profile'. canceled _ false. dialogue openInWorld! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 22:45'! openDialogOn: anIRCConnectionProfile "Get here after we do the server dialog window." | dialogue | editing _ true. profile _ anIRCConnectionProfile. dialogue _ SystemWindow new. dialogue model: self. self buildDialogOn: dialogue. dialogue setLabel: 'edit a profile'. canceled _ false. dialogue openInWorld! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 22:45'! openForAddWithOptions: options "Get a server first." editing _ false. profile _ IRCConnectionProfile default. profile options: options. self serverButton! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 21:04'! parentDialog: morph parentDialog _ morph! ! !IRCProfileDialog methodsFor: 'as yet unclassified' stamp: 'sbw 3/4/2002 07:50'! referenceConnection: anIRCConnectionEnhanced referenceConnection _ anIRCConnectionEnhanced! ! !IRCProfileDialog methodsFor: 'profile name' stamp: 'sbw 3/4/2002 23:00'! buildProfileNamePane ^ self buildEntryPane: 'Profile Name:' get: #profileName set: #profileName: entryWidth: 400 canEdit: true! ! !IRCProfileDialog methodsFor: 'profile name' stamp: 'sbw 3/4/2002 00:36'! profileName ^ profile name! ! !IRCProfileDialog methodsFor: 'profile name' stamp: 'sbw 3/7/2002 02:26'! profileName: aString profile name: aString asString withBlanksTrimmed. ^ true! ]style[(13 7 3 7 7 7 32 4)f1b,f1cblue;b,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;! ! !IRCProfileDialog methodsFor: 'server address' stamp: 'sbw 3/4/2002 23:18'! buildServerAddressPane | alignPane btn | alignPane _ self buildEntryPane: 'Server Address:' get: #serverAddressString set: #serverAddressString: entryWidth: 300 canEdit: false. "Add a server button choice here." btn _ PluggableButtonMorph on: self getState: nil action: #serverButton. Preferences alternativeWindowLook ifTrue: [btn borderWidth: 2; borderColor: #raised]. btn label: 'Choose Server'. btn useRoundedCorners. btn color: Color veryVeryLightGray. btn onColor: Color white offColor: Color veryVeryLightGray. alignPane addTransparentSpacerOfSize: 10@0. alignPane addMorphBack: btn. btn setBalloonText: 'Opens up a server list dialog.'. ^ alignPane! ! !IRCProfileDialog methodsFor: 'server address' stamp: 'sbw 3/4/2002 00:37'! serverAddressString ^ profile server address! ! !IRCProfileDialog methodsFor: 'server address' stamp: 'sbw 3/4/2002 01:29'! serverAddressString: aString ^ false! ! !IRCProfileDialog methodsFor: 'nick' stamp: 'sbw 3/4/2002 23:00'! buildNickPane ^ self buildEntryPane: 'Nick:' get: #nick set: #nick: entryWidth: 400 canEdit: true! ! !IRCProfileDialog methodsFor: 'nick' stamp: 'sbw 3/4/2002 00:37'! nick ^ profile nick! ! !IRCProfileDialog methodsFor: 'nick' stamp: 'sbw 3/4/2002 01:30'! nick: aString profile nick: aString. ^true! ! !IRCProfileDialog methodsFor: 'full name' stamp: 'sbw 3/4/2002 23:00'! buildFullNamePane ^ self buildEntryPane: 'Full Name:' get: #fullName set: #fullName: entryWidth: 400 canEdit: true! ! !IRCProfileDialog methodsFor: 'full name' stamp: 'sbw 3/4/2002 00:37'! fullName ^ profile fullName! ! !IRCProfileDialog methodsFor: 'full name' stamp: 'sbw 3/4/2002 01:30'! fullName: str profile fullName: str. ^true! ! !IRCProfileDialog methodsFor: 'user name' stamp: 'sbw 3/4/2002 23:00'! buildUserNamePane ^ self buildEntryPane: 'User Name:' get: #userName set: #userName: entryWidth: 400 canEdit: true! ! !IRCProfileDialog methodsFor: 'user name' stamp: 'sbw 3/4/2002 00:38'! userName ^ profile userName! ! !IRCProfileDialog methodsFor: 'user name' stamp: 'sbw 3/4/2002 01:30'! userName: str profile userName: str. ^true! ! !IRCProfileDialog methodsFor: 'common entry' stamp: 'sbw 11/16/2002 16:12'! buildEntryPane: tag get: get set: set entryWidth: entryWidth canEdit: boolean | alignMorph labelMorph textEntry | alignMorph _ AlignmentMorph newRow. alignMorph borderWidth: 0; hResizing: #spaceFill; vResizing: #shrinkWrap; color: Color transparent. labelMorph _ TextMorph new. labelMorph beAllFont: ((TextStyle default fontOfSize: 18) emphasized: 1); contents: tag; color: Color black. labelMorph lock. alignMorph addMorphBack: labelMorph. alignMorph addTransparentSpacerOfSize: self labelRowWidth - labelMorph width @ 0. textEntry _ (boolean ifTrue: [PluggableTextMorph] ifFalse: [PluggableTextMorphHybrid]) on: self text: get accept: (boolean ifTrue: [set]). textEntry color: (boolean ifTrue: [Color white] ifFalse: [Color transparent]); extent: entryWidth @ self editRowHeight; hideScrollBarIndefinitely; acceptOnCR: true. alignMorph addMorphBack: textEntry. ^ alignMorph! ! !IRCProfileDialog methodsFor: 'common entry' stamp: 'sbw 3/4/2002 01:25'! okToChange | subs | canceled ifTrue: [^true]. subs _ self topView allMorphs select: [:m | m isKindOf: PluggableTextMorph]. subs do: [:textMorph | textMorph hasUnacceptedEdits ifTrue: [^ false]]. ^ true! ! !IRCProfileDialog methodsFor: 'local action buttons' stamp: 'sbw 11/16/2002 12:29'! buildActionButtons | buttons alignMorph | buttons _ self buttonRow. Preferences alternativeWindowLook ifTrue: [buttons submorphsDo: [:m | m borderWidth: 2; borderColor: #raised]]. alignMorph _ AlignmentMorph newRow hResizing: #spaceFill; vResizing: #shrinkWrap; layoutInset: 0; borderWidth: 0; layoutPolicy: ProportionalLayout new. alignMorph addMorph: buttons fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ 0 corner: 0 @ 0)). ^ alignMorph! ! !IRCProfileDialog methodsFor: 'local action buttons' stamp: 'sbw 11/16/2002 16:13'! buttonRow | aRow btn | aRow _ AlignmentMorph newRow beSticky. aRow color: Color paleTan. aRow clipSubmorphs: true. aRow layoutInset: 10 @ 1; cellInset: 14. self buttonSpecs do: [:spec | btn _ PluggableButtonMorph on: self getState: nil action: spec second. btn color: Color white; hResizing: #spaceFill; vResizing: #spaceFill; useRoundedCorners; setNameTo: spec third; label: spec first asString. btn onColor: Color veryVeryLightGray offColor: Color white. aRow addMorphBack: btn. btn setBalloonText: spec fourth]. ^ aRow! ! !IRCProfileDialog methodsFor: 'local action buttons' stamp: 'sbw 3/4/2002 00:42'! buttonSpecs "button label, selector, button name, help text" ^ #(#('Okay' #profileSelected 'okayButton' 'Accept these values for a new profile.') #('Cancel' #cancelSelected 'cancelButton' 'Discard values') )! ! !IRCProfileDialog methodsFor: 'local action buttons' stamp: 'sbw 3/4/2002 01:25'! cancelSelected canceled _ true. self topView delete! ! !IRCProfileDialog methodsFor: 'local action buttons' stamp: 'sbw 3/5/2002 00:25'! profileSelected | targetName | editing ifTrue: [parentDialog editedProfile: profile] ifFalse: [targetName _ profile name. (referenceConnection class hasProfileNamed: targetName) ifTrue: [^ self inform: 'That profile name is already taken.'] ifFalse: [parentDialog addProfile: profile]]. self topView delete! ! !IRCProfileDialog methodsFor: 'local action buttons' stamp: 'sbw 3/3/2002 21:04'! windowIsClosing parentDialog isNil ifTrue: [^ nil]. parentDialog showWindow! ! !IRCProfileDialog methodsFor: 'remote window actions' stamp: 'sbw 3/3/2002 21:13'! hideWindow self topView hide! ! !IRCProfileDialog methodsFor: 'remote window actions' stamp: 'sbw 3/4/2002 23:46'! noServerSelected "The user aborted the selection from the server dialog. Open our own window and get busy." self topView isNil ifTrue: [self openDialog] ifFalse: [self showWindow] ! ! !IRCProfileDialog methodsFor: 'remote window actions' stamp: 'sbw 3/4/2002 23:45'! serverButton "From dialog" | dlg | dlg _ IRCServerDialog on: referenceConnection class serversListByGroups. dlg parentDialog: self. self topView isNil ifFalse: [self hideWindow]. dlg openDialog! ! !IRCProfileDialog methodsFor: 'remote window actions' stamp: 'sbw 3/3/2002 21:13'! showWindow self topView show! ! !IRCProfileDialog methodsFor: 'remote window actions' stamp: 'sbw 3/4/2002 23:47'! useValuesFromServer: anIRCServer port: portNumber "We have a valid choice from the server dialog. Open our own window and get busy." anIRCServer port: portNumber. profile server: anIRCServer. profile name: anIRCServer nameToUseForProfile. self topView isNil ifTrue: [self openDialog] ifFalse: [self showWindow; changed: #profileName; changed: #serverAddressString]! ! !IRCProtocolMessage methodsFor: 'access' stamp: 'sbw 2/26/2002 16:18'! commandContainsSpecialCharacter ^command first = self class specialCommandCharacter! ! !IRCProtocolMessage methodsFor: 'access' stamp: 'sbw 3/2/2002 09:59'! isLocal ^isLocal! ! !IRCProtocolMessage methodsFor: 'access' stamp: 'sbw 2/26/2002 17:30'! isSpecial ^isSpecial! ! !IRCProtocolMessage methodsFor: 'access' stamp: 'sbw 2/26/2002 17:32'! specialConvertedForm ^ self arguments last! ! !IRCProtocolMessage methodsFor: 'private-initialization' stamp: 'sbw 3/2/2002 16:26'! checkForCommandHandling "We will intercept /commands entered by the user and adjust as we can. See IRCConnectionEnhanced initializeOutgoingMessageHandlers" command first = $/ ifTrue: [command _ command copyFrom: 2 to: command size. self performSlashCommandHandler: command]! ! !IRCProtocolMessage methodsFor: 'private-initialization' stamp: 'sbw 3/2/2002 10:05'! checkForLocalCommandHandling "Local commands are never sent to the connection socket (over the wire). See IRCConnectionEnhanced localMessageHandlers." isLocal _ IRCConnectionEnhanced localMessageHandlers keys includes: command asLowercase. ! ! !IRCProtocolMessage methodsFor: 'private-initialization' stamp: 'sbw 4/25/2002 09:19'! checkForSpecialCommandHandling "See http://www.irchelp.org/irchelp/rfc/ctcpspec.html" | lastArg controlChar stream convertedArg savedCommand | isSpecial _ IRCConnectionEnhanced specialCommands includes: command. isSpecial ifFalse: [^ nil]. savedCommand _ command asUppercase copy. command _ 'PRIVMSG'. lastArg _ arguments last. controlChar _ self class specialCommandCharacter. stream _ ReadWriteStream on: ''. stream nextPut: controlChar; nextPutAll: savedCommand; nextPut: $ ; nextPutAll: lastArg; nextPut: controlChar. convertedArg _ stream contents. arguments at: arguments size put: convertedArg! ! !IRCProtocolMessage methodsFor: 'private-initialization' stamp: 'sbw 3/2/2002 16:31'! prefix: aString command: anotherString arguments: aCollection prefix _ aString. command _ anotherString asIRCLowercase. arguments _ aCollection. self checkForCommandHandling. self checkForLocalCommandHandling. self checkForSpecialCommandHandling! ! !IRCProtocolMessage methodsFor: 'encoding' stamp: 'sbw 2/26/2002 16:52'! asString "encode in the format used for transferral over the network" ^ String streamContents: [:stream | prefix ifNotNil: [stream nextPut: $:. stream nextPutAll: prefix. stream space]. stream nextPutAll: command asUppercase. stream space. arguments isEmpty ifFalse: ["print out all but the last argument" (arguments copyFrom: 1 to: arguments size - 1) do: [:arg | stream nextPutAll: arg. stream space]. "print the last as a trailer, just to be safe" stream nextPut: $:. stream nextPutAll: arguments last]. stream nextPut: Character cr. stream nextPut: Character lf]! ! !IRCProtocolMessage methodsFor: 'encoding' stamp: 'sbw 3/2/2002 11:20'! printOn: stream | indent argCount | indent _ ' '. super printOn: stream. prefix ifNotNil: [stream cr; nextPutAll: indent. stream nextPutAll: 'PREFIX: '. stream nextPut: $:. stream nextPutAll: prefix. stream space]. stream cr; nextPutAll: indent; nextPutAll: 'COMMAND: '; nextPutAll: command asUppercase. stream space. arguments isEmpty ifFalse: ["print out all but the last argument" argCount _ 0. stream cr; nextPutAll: indent; nextPutAll: 'ARGUMENTS:'. (arguments copyFrom: 1 to: arguments size - 1) do: [:arg | argCount _ argCount + 1. stream cr; nextPutAll: indent; nextPutAll: indent. stream nextPutAll: argCount printString , ': '. stream nextPutAll: arg. stream space]. "print the last as a trailer, just to be safe" argCount _ argCount + 1. stream cr; nextPutAll: indent; nextPutAll: indent. stream nextPutAll: argCount printString , ': '. stream nextPut: $:. self isLocal ifTrue: [stream nextPutAll: 'an' , arguments last class name] ifFalse: [stream nextPutAll: arguments last]]! ! !IRCProtocolMessage methodsFor: 'slash commands translation' stamp: 'sbw 3/2/2002 16:21'! performSlashCommandHandler: aString | handler | handler _ IRCConnectionEnhanced outgoingMessageHandlers at: aString asLowercase asSymbol ifAbsent: [#processInvalidCommand]. self perform: handler! ! !IRCProtocolMessage methodsFor: 'slash commands translation' stamp: 'sbw 3/2/2002 16:21'! processInvalidCommand! ! !IRCProtocolMessage methodsFor: 'slash commands translation' stamp: 'sbw 3/2/2002 17:37'! processJoinCommand arguments size > 1 ifTrue: [arguments _ Array with: arguments second]. ! ! !IRCProtocolMessage methodsFor: 'slash commands translation' stamp: 'sbw 3/2/2002 17:01'! processNickCommand "Everything is okay except we need to strip off the first argument if there are 2." arguments size > 1 ifTrue: [ arguments _ Array with: arguments second]! ! !IRCProtocolMessage methodsFor: 'slash commands translation' stamp: 'sbw 3/2/2002 16:20'! processPartCommand! ! !IRCProtocolMessage methodsFor: 'slash commands translation' stamp: 'sbw 3/2/2002 17:00'! processWhoisCommand "Everything is okay except we need to strip off the first argument if there is a 2nd." arguments size > 1 ifTrue: [ arguments _ Array with: arguments second]! ]style[(19 2 116 2 9 3 5 7 9 8)f1b,f1,f1c130028000,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1! ! !IRCProtocolMessage class methodsFor: 'instance creation' stamp: 'sbw 3/2/2002 17:07'! fromString: aString "parse an IRC message from its network format" | remainder trailerStart trailer tokens command arguments prefix idx | remainder _ aString. "strip the CRLF" idx _ remainder indexOf: Character cr. idx > 0 ifTrue: [remainder _ remainder copyFrom: 1 to: idx - 1]. idx _ remainder indexOf: Character lf. idx > 0 ifTrue: [remainder _ remainder copyFrom: 1 to: idx - 1]. "check if there is a trailer" trailerStart _ remainder indexOf: $: startingAt: 2 ifAbsent: [0]. trailerStart > 0 ifTrue: [trailer _ remainder copyFrom: trailerStart + 1 to: remainder size. remainder _ remainder copyFrom: 1 to: trailerStart - 1]. "divide the rest of the string up between spaces" tokens _ remainder findTokens: ' '. "the command is the first token..." command _ tokens removeFirst. command first = $: ifTrue: ["...unless it starts with a $:, in which case there is a prefix and the command is the second token" prefix _ command copyFrom: 2 to: command size. "copy without the leading :" command _ tokens removeFirst]. "the arguments are the remaining tokens, plus the trailer if any" arguments _ tokens. trailer ifNotNil: [arguments _ arguments copyWith: trailer]. ^ self prefix: prefix command: command arguments: arguments! ! !IRCProtocolMessage class methodsFor: 'instance creation' stamp: 'sbw 2/20/2002 18:26'! fromStringDebug: aString true ifFalse: ['self halt']. ^ self fromString: aString! ! !IRCProtocolMessage class methodsFor: 'instance creation' stamp: 'sbw 2/26/2002 16:17'! specialCommandCharacter ^ Character value: 1.! ! !IRCServer methodsFor: 'access' stamp: 'sbw 1/11/2003 18:40'! = anIRCServer anIRCServer == nil ifTrue: [^false]. ^ ((self nameToUseForProfile = anIRCServer nameToUseForProfile and: [self address = anIRCServer address]) and: [self group = anIRCServer group]) and: [self ports = anIRCServer ports]! ]style[(2 11 3 36 6 4 23 11 31 4 11 11 20 4 9 11 17 4 9 11 7)f1b,f1cblue;b,f1,f1cblue;b,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1! ! !IRCServer methodsFor: 'access' stamp: 'sbw 5/16/1999 10:27'! address ^address! ! !IRCServer methodsFor: 'access' stamp: 'sbw 5/16/1999 10:27'! address: aString address _ aString! ! !IRCServer methodsFor: 'access' stamp: 'sbw 3/3/2002 10:33'! defaultPort ^self ports last! ! !IRCServer methodsFor: 'access' stamp: 'sbw 5/16/1999 10:49'! group ^group! ! !IRCServer methodsFor: 'access' stamp: 'sbw 5/16/1999 10:49'! group: aString group _ aString! ! !IRCServer methodsFor: 'access' stamp: 'sbw 1/11/2003 18:31'! hash ^ self nameToUseForProfile hash + self address hash + self group hash + self ports hash! ! !IRCServer methodsFor: 'access' stamp: 'sbw 3/4/2002 12:08'! nameToUseForProfile | part2 part1 | part2 _ self serverName withBlanksTrimmed. self group isNil ifTrue: [^part2]. self group isEmpty ifTrue: [^part2]. part1 _ self group withBlanksTrimmed. ^part1, ' ', part2! ! !IRCServer methodsFor: 'access' stamp: 'sbw 3/3/2002 10:44'! port port == nil ifTrue: [port _ self defaultPort]. ^port! ! !IRCServer methodsFor: 'access' stamp: 'sbw 3/3/2002 10:44'! port: num port _ num! ! !IRCServer methodsFor: 'access' stamp: 'sbw 5/16/1999 10:27'! ports ^ports! ! !IRCServer methodsFor: 'access' stamp: 'sbw 5/16/1999 10:28'! ports: aCollection ports _ aCollection! ! !IRCServer methodsFor: 'access' stamp: 'sbw 2/29/2000 21:17'! serverName ^serverName! ! !IRCServer methodsFor: 'access' stamp: 'sbw 2/29/2000 21:17'! serverName: aString serverName _ aString! ! !IRCServer methodsFor: 'printing' stamp: 'sbw 2/29/2000 22:05'! printOn: aStream super printOn: aStream. aStream cr; nextPutAll: ' '; nextPutAll: 'Group: '; nextPutAll: self group asString; cr; nextPutAll: ' '; nextPutAll: 'Name: '; nextPutAll: self serverName asString; cr; nextPutAll: ' '; nextPutAll: 'Addr: '; nextPutAll: self address asString. self ports do: [:each | aStream cr; nextPutAll: ' Port: '; nextPutAll: each printString]! ! !IRCServer methodsFor: 'printing' stamp: 'sbw 3/4/2002 00:55'! profileListString | stream | stream _ ReadWriteStream on: ''. stream nextPutAll: self address asString; nextPutAll: ':'; nextPutAll: self port printString. ^ stream contents! ! !IRCServer methodsFor: 'printing' stamp: 'sbw 3/4/2002 00:55'! profileString | stream | stream _ ReadWriteStream on: ''. stream nextPut: $"; nextPutAll: self serverName asString; nextPut: $"; nextPutAll: ' '; nextPutAll: self profileListString contents. ^ stream contents! ! !IRCServer methodsFor: 'parse mIRC entries' stamp: 'sbw 2/29/2000 21:25'! configureFrommIRC: aString " contains a line from the mIRC servers.ini file. We will parse it to configure ourselves." "Layout of test is: n24=Asylumnet: EU, UK, LondonSERVER:london.uk.eu.asylumnet.org:7070,7777,6660,6663,6666,6669,7000GROUP:asylumnet or n1=Random US DALnet serverSERVER:irc.dal.net:7000GROUP:01 or n149=Gammaforce: US, SphinxSERVER:sphinx.or.us.gammaforce.org 6660,6663,6669GROUP:gammaforce or n103=EFnet: EU, NO, OsloSERVER:oslo efnet.telia.no:6667GROUP:efnet" | tempString groupName theServerName serverAddress portNumbers | tempString _ self extractGroupAndServerStringFrom: aString. groupName _ self extractGroupNameFrom: tempString. theServerName _ self extractServerNameFrom: tempString. tempString _ self extractServerAddressAndPortStringFrom: aString. serverAddress _ self extractServerAddressFrom: tempString. portNumbers _ self extractPortsFrom: tempString. self address: serverAddress. self group: groupName. self ports: portNumbers. self serverName: theServerName! ! !IRCServer methodsFor: 'parse mIRC entries' stamp: 'sbw 2/24/2002 20:30'! extractGroupAndServerStringFrom: aString "Layout of is: n24=Asylumnet: EU, UK, LondonSERVER:london.uk.eu.asylumnet.org:7070,7777,6660,6663,6666,6669,7000GROUP:asylumnet or n1=Random US DALnet serverSERVER:irc.dal.net:7000GROUP:01 or n149=Gammaforce: US, SphinxSERVER:sphinx.or.us.gammaforce.org 6660,6663,6669GROUP:gammaforce or n103=EFnet: EU, NO, OsloSERVER:oslo efnet.telia.no:6667GROUP:efnet" | index start end | index _ aString indexOf: $= startingAt: 1. start _ index + 1. index _ aString findString: self class serverStringTag startingAt: start caseSensitive: true. end _ index - 1. ^ aString copyFrom: start to: end! ! !IRCServer methodsFor: 'parse mIRC entries' stamp: 'sbw 5/18/1999 14:51'! extractGroupNameFrom: aString "Layout of is: 'Asylumnet: EU, UK, London' or 'Random US DALnet server' or 'Gammaforce: US, Sphinx' or'EFnet: EU, NO, Oslo'" | start colonPos end | start _ 1. colonPos _ aString indexOf: $: startingAt: start. colonPos > 0 ifTrue: [ end _ colonPos - 1. ^aString copyFrom: start to: end] ifFalse: [^''] "none defined"! ! !IRCServer methodsFor: 'parse mIRC entries' stamp: 'sbw 5/18/1999 19:38'! extractPortsFrom: aString "Layout of is: london.uk.eu.asylumnet.org:7070,7777,6660,6663,6666,6669,7000 or irc.dal.net:7000 or sphinx.or.us.gammaforce.org 6660,6663,6669 or oslo efnet.telia.no:6667 or wildfire.magicstar.net6667" "The only safe way to do this is scan from the tail looking for a non-digit char that is not a comma or dash." | posn char start end portsString portTokens portNumbers index first last | posn _ aString size. [ char _ aString at: posn. (posn > 1 and: [ ((char isDigit or: [char = $,])) or: [char = $-]])] whileTrue: [posn _ posn - 1]. start _ posn + 1. end _ aString size. portsString _ aString copyFrom: start to: end. portTokens _ portsString findTokens: ','. portNumbers _ OrderedCollection new. portTokens do: [:each | index _ each indexOf: $-. index > 0 ifTrue: [ "range" first _ (each copyFrom: 1 to: (index - 1)) asNumber. last _ (each copyFrom: (index + 1) to: each size) asNumber. first to: last do: [:intval | portNumbers add: intval] ] ifFalse: [portNumbers add: each asNumber]]. ^portNumbers ! ! !IRCServer methodsFor: 'parse mIRC entries' stamp: 'sbw 2/24/2002 20:46'! extractServerAddressAndPortStringFrom: aString "Layout of is: n24=Asylumnet: EU, UK, LondonSERVER:london.uk.eu.asylumnet.org:7070,7777,6660,6663,6666,6669,7000GROUP:asylumnet or n1=Random US DALnet serverSERVER:irc.dal.net:7000GROUP:01 or n149=Gammaforce: US, SphinxSERVER:sphinx.or.us.gammaforce.org 6660,6663,6669GROUP:gammaforce or n103=EFnet: EU, NO, OsloSERVER:oslo efnet.telia.no:6667GROUP:efnet" | start index offset end | start _ 1. index _ aString findString: self class serverStringTag startingAt: start caseSensitive: true. offset _ self class serverStringTag size. start _ index + offset + 1. index _ aString findString: self class groupStringTag startingAt: start caseSensitive: true. end _ index = 0 ifTrue: [aString size] ifFalse: [index - 1]. ^ aString copyFrom: start to: end! ]style[(39 7 3 411 3 23 4 5 3 1 3 5 3 7 17 4 39 5 20 4 3 6 3 4 30 5 3 5 3 6 3 1 3 5 3 7 17 4 38 5 20 4 3 3 50 5 3 2 5 7 11 5 5 3)f1b,f1cblue;b,f1,f1c130028000,f1,f1cblue;i,f1,f1cblue;i,f1,f1c180180104,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c180180104,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c180180104,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i! ! !IRCServer methodsFor: 'parse mIRC entries' stamp: 'sbw 5/18/1999 19:26'! extractServerAddressFrom: aString "Layout of is: london.uk.eu.asylumnet.org:7070,7777,6660,6663,6666,6669,7000 or irc.dal.net:7000 or sphinx.or.us.gammaforce.org 6660,6663,6669 or oslo efnet.telia.no:6667 or wildfire.magicstar.net6667" "The only safe way to do this is scan from the tail looking for a non-digit char that is not a comma or dash." | posn char start end | posn _ aString size. [ char _ aString at: posn. (posn > 1 and: [ ((char isDigit or: [char = $,])) or: [char = $-]])] whileTrue: [posn _ posn - 1]. start _ 1. end _ posn - 1. ^aString copyFrom: start to: end! ! !IRCServer methodsFor: 'parse mIRC entries' stamp: 'sbw 5/18/1999 14:54'! extractServerNameFrom: aString "Layout of is: 'Asylumnet: EU, UK, London' or 'Random US DALnet server' or 'Gammaforce: US, Sphinx' or'EFnet: EU, NO, Oslo'" | start index end | start _ 1. index _ aString indexOf: $: startingAt: start. index > 0 ifTrue: [start _ index + 1]. end _ aString size. ^aString copyFrom: start to: end! ! !IRCServer class methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 20:27'! groupStringTag ^ 'GROUP'! ! !IRCServer class methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 20:28'! serverStringTag ^ 'SERVER'! ! !IRCServer class methodsFor: 'as yet unclassified' stamp: 'sbw 2/29/2000 15:46'! undefinedGroupName ^''! ! !IRCServer class methodsFor: 'instance creation' stamp: 'sbw 5/18/1999 13:33'! from: aString "Answers either a valid server or nil if the line is no good." | candidate | aString isNil ifTrue: [^nil]. aString isEmpty ifTrue: [^nil]. aString first = $; ifTrue: [^nil]. aString first = $[ ifTrue: [^nil]. candidate _ self new. candidate configureFrommIRC: aString. ^candidate! ! !IRCServer class methodsFor: 'test' stamp: 'sbw 1/11/2003 18:13'! addOpenProjectsNetServer "IRCServer addOpenProjectsNetServer" IRCConnectionEnhanced addServer: (IRCServer from: 'Open Projects NetSERVER:IRC.FREENODE.NET:6667') ! ! !IRCServer class methodsFor: 'test' stamp: 'sbw 3/3/2002 10:27'! defaultServer ^IRCServer from: 'Open Projects NetSERVER:irc.openprojects.net:6667'! ! !IRCServer class methodsFor: 'test' stamp: 'sbw 2/24/2002 21:02'! mircServersIni "Contents of the servers.ini file that mIRC uses to define IRC servers." ^'; Please report errors and broken servers to servers@mirc.com ; March 22nd, 2001 [servers] n0=Random US DALnet serverSERVER:irc.dal.net:7000,6667,6668,6669GROUP:01 n1=powertech.no.eu.dal.netSERVER:powertech.no.eu.dal.net:6667 n2=Random EU DALnet serverSERVER:irc.eu.dal.net:6667GROUP:02 n3=Random US EFnet serverSERVER:us.rr.efnet.net:6667GROUP:03 n4=Random EU EFnet serverSERVER:eu.rr.efnet.net:6667GROUP:04 n5=Random CA EFnet serverSERVER:ca.rr.efnet.net:6667GROUP:05 n6=Random AU EFnet serverSERVER:au.rr.efnet.net:6667GROUP:06 n7=Random US IRCnet serverSERVER:us.ircnet.org:6665,6666,6668GROUP:07 n8=Random EU IRCnet serverSERVER:eu.ircnet.org:6665,6666,6668GROUP:08 n9=Random AU IRCnet serverSERVER:au.ircnet.org:6667GROUP:09 n10=Random US Undernet serverSERVER:us.undernet.org:6667GROUP:10 n11=Random EU Undernet serverSERVER:eu.undernet.org:6667GROUP:11 n12=Accessirc: Random serverSERVER:irc.accessirc.net:6667GROUP:Accessirc n13=Acestar: Random serverSERVER:irc.acestar.org:6667GROUP:Acestar n14=Action-IRC: Random serverSERVER:irc.action-irc.net:6661,6662,6664GROUP:Action-IRC n15=Afternet: Random serverSERVER:irc.afternet.org:6667GROUP:Afternet n16=Alternativenet: Random serverSERVER:irc.altnet.org:6667GROUP:Alternativenet n17=Animenet: Random serverSERVER:irc.animenet.org:6663,6666,6668GROUP:Animenet n18=AnotherNet: Random serverSERVER:irc.another.net:6667,7000GROUP:Anothernet n19=ArabChat: Random serverSERVER:irc.arabchat.org:6663,6664,6665GROUP:ArabChat n20=AsiaChat: Random serverSERVER:irc.asiachat.org:6668,6669,7000GROUP:AsiaChat n21=AstroLink: Random serverSERVER:irc.astrolink.org:6661,6663,6665GROUP:AstroLink n22=Asylumnet: Random serverSERVER:irc.asylum-net.org:6661,6664,6665GROUP:Asylumnet n23=Austnet: Random AU serverSERVER:au.austnet.org:6667GROUP:Austnet n24=Austnet: Random NZ serverSERVER:nz.austnet.org:6667GROUP:Austnet n25=Austnet: Random SG serverSERVER:sg.austnet.org:6667GROUP:Austnet n26=Austnet: Random US serverSERVER:us.austnet.org:6667GROUP:Austnet n27=AwesomeChat: Random serverSERVER:irc.awesomechat.net:6662,6663,6668GROUP:AwesomeChat n28=Axenet: Random serverSERVER:irc.axenet.org:6660,6661,6662GROUP:Axenet n29=BeyondIRC: Random serverSERVER:irc.beyondirc.net:6660,6664,6668GROUP:Beyondirc n30=Blabbernet: Random serverSERVER:irc.blabber.net:6667,7000GROUP:Blabbernet n31=Blitzed: Random serverSERVER:irc.blitzed.org:6667,7000GROUP:Blitzed n32=Bohemians: Random serverSERVER:irc.bohemians.org:6667,7000GROUP:Bohemians n33=Bohika: Random serverSERVER:irc.bohika.net:6663,6665,6666GROUP:Bohika n34=Brasirc: Random serverSERVER:irc.brasirc.net:6666,6667GROUP:Brasirc n35=Brasirc: BR, PA, BelemSERVER:irc.libnet.com.br:6666,6668,7777GROUP:Brasirc n36=Brasirc: BR, SC, FlorianopolisSERVER:irc.intergate.com.br:6667GROUP:Brasirc n37=Brasnet: Random European serverSERVER:eu.brasnet.org:6665,6666,6668GROUP:Brasnet n38=Brasnet: Random serverSERVER:irc.brasnet.org:6665,6666,6668GROUP:Brasnet n39=Brasnet: Random US serverSERVER:us.brasnet.org:6665,6666,6668GROUP:Brasnet n40=Castlenet-irc: Random serverSERVER:irc.castlenet-irc.org:6667GROUP:Castlenet-irc n41=CCnet: Random serverSERVER:irc.cchat.net:6667,7000GROUP:CCnet n42=CCnet: US, TX, DallasSERVER:irc2.cchat.net:6667,7000GROUP:CCnet n43=ChatArea: Random serverSERVER:irc.chatarea.net:6667GROUP:ChatArea n44=Chatcafe: Random serverSERVER:irc.chatcafe.net:6667GROUP:Chatcafe n45=ChatCentral2: Random serverSERVER:irc.cc2.org:6665,6666,6668GROUP:ChatCentral2 n46=ChatCircuit: Random serverSERVER:irc.chatcircuit.com:6667GROUP:ChatCircuit n47=ChatCity: Random serverSERVER:irc.chatcity.org:6668,6669,7000GROUP:ChatCity n48=ChatIRC: Random serverSERVER:irc.chatirc.net:6667GROUP:ChatIRC n49=Chatlink: Random serverSERVER:irc.chatlink.org:6667GROUP:Chatlink n50=Chatlink: US, CA, DavisSERVER:cool.ca.us.chatlink.org:6667GROUP:Chatlink n51=Chatlink: US, OH, AthensSERVER:dream.oh.us.chatlink.org:6667GROUP:Chatlink n52=Chatnet: Random AU serverSERVER:au.chatnet.org:6667GROUP:Chatnet n53=Chatnet: Random EU serverSERVER:eu.chatnet.org:6667GROUP:Chatnet n54=Chatnet: Random US serverSERVER:us.chatnet.org:6667GROUP:Chatnet n55=Chatpinoy: Random serverSERVER:irc.chatpinoy.com:6667GROUP:Chatpinoy n56=ChatPR: Random serverSERVER:irc.chatpr.org:6667GROUP:ChatPR n57=Chatroom: Random serverSERVER:irc.chatroom.org:6667GROUP:Chatroom n58=ChatX: Random serverSERVER:irc.chatx.net:6667GROUP:ChatX n59=Circanet: Random serverSERVER:irc.circanet.org:6664,6666,6668GROUP:Circanet n60=CNN: CNN News discussionsSERVER:chat.cnn.com:6668,6669,7000GROUP:CNN n61=Coolchat: Random serverSERVER:irc.coolchat.net:6667GROUP:Coolchat n62=Criten: Random serverSERVER:irc.criten.net:6667GROUP:Criten n63=Cyberchat: Random serverSERVER:irc.cyberchat.org:6667,6668GROUP:Cyberchat n64=Cyberchat-irc: Random serverSERVER:irc.cyberchat-irc.net:6667GROUP:Cyberchat-irc n65=CyGanet: Random serverSERVER:irc.cyga.net:6667GROUP:CyGanet n66=DALnet: AS, MY, Kuala LumpurSERVER:coins.dal.net:6664,6666,6668GROUP:DALnet n67=DALnet: AU, AdelaideSERVER:ozbytes.dal.net:6667,7000GROUP:DALnet n68=DALnet: CA, BC, VancouverSERVER:vancouver.dal.net:6661,6663,6666GROUP:DALnet n69=DALnet: EU, DE, FrankfurtSERVER:nexgo.de.eu.dal.net:6664,6668,6669GROUP:DALnet n70=DALnet: EU, NO, OsloSERVER:powertech.no.eu.dal.net:6666,6667,7000GROUP:DALnet n71=DALnet: EU, NO, TromsoSERVER:viking.dal.net:6666,6668,6669GROUP:DALnet n72=DALnet: EU, SE, GoteborgSERVER:ced.dal.net:6667,7000GROUP:DALnet n73=DALnet: EU, SE, StockholmSERVER:paranoia.dal.net:6661,6662,6664GROUP:DALnet n74=DALnet: EU, UK, LondonSERVER:defiant.dal.net:6666,6668,6669GROUP:DALnet n75=DALnet: US, FL, HollywoodSERVER:sodre.dal.net:6661,6662,6663GROUP:DALnet n76=DALnet: US, IN, HebronSERVER:hebron.dal.net:6662,6666,6669GROUP:DALnet n77=DALnet: US, MD, ManchesterSERVER:qis.md.us.dal.net:6662,6663,6668GROUP:DALnet n78=DALnet: US, NY, New York CitySERVER:liberty.dal.net:6663,6666,6668GROUP:DALnet n79=DALnet: US, OH, ToledoSERVER:glass.dal.net:6660,6661,6662GROUP:DALnet n80=DALnet: US, OK, TulsaSERVER:webzone.dal.net:6665,6666,6669GROUP:DALnet n81=Darkernet: Random serverSERVER:irc.darker.net:6667GROUP:Darkernet n82=Darkfire: Random serverSERVER:irc.darkfire.net:6667,7000,8000GROUP:Darkfire n83=Darkfyre: Random serverSERVER:irc.darkfyre.net:6667GROUP:Darkfyre n84=DarkMyst: Random serverSERVER:irc.darkmyst.org:6667GROUP:DarkMyst n85=Darktree: Random serverSERVER:irc.darktree.net:6667GROUP:Darktree n86=Deepspace: Disability networkSERVER:irc.deepspace.org:6667GROUP:Deepspace n87=Different: Random serverSERVER:irc.different.net:6667GROUP:Different n88=Digarix: Random serverSERVER:irc.digarix.net:6667GROUP:Digarix n89=Digitalirc: Random serverSERVER:irc.digitalirc.net:6667GROUP:Digitalirc n90=Dobbernet: Random serverSERVER:irc.dobber.net:6667GROUP:Dobbernet n91=Dreamcast: Random serverSERVER:irc0.dreamcast.com:6667GROUP:Dreamcast n92=Dreamnet: Random serverSERVER:irc.dreamnet.org:6664,6665,6666GROUP:Dreamnet n93=DS2: Random serverSERVER:irc.ds2.net:6667GROUP:DS2 n94=Duh-net: Random serverSERVER:irc.duh-net.org:6667GROUP:Duhnet n95=Dynastynet: Random serverSERVER:irc.dynastynet.net:6667GROUP:Dynastynet n96=EFnet: CA, AB, EdmontonSERVER:irc.powersurfr.com:6667GROUP:EFnet n97=EFnet: CA, ON, TorontoSERVER:irc2.magic.ca:6667GROUP:EFnet n98=EFnet: CA, QB, MontrealSERVER:irc.etsmtl.ca:6667GROUP:EFnet n99=EFnet: EU, FI, HelsinkiSERVER:efnet.cs.hut.fi:6667GROUP:EFnet n100=EFnet: EU, FR, ParisSERVER:irc.isdnet.fr:6667,6668,6669GROUP:EFnet n101=EFnet: EU, NL, AmsterdamSERVER:efnet.vuurwerk.nl:6667GROUP:EFnet n102=EFnet: EU, NO, HomelienSERVER:irc.homelien.no:6666,7000,7001GROUP:EFnet n103=EFnet: EU, RU, MoscowSERVER:irc.rt.ru:6664,6665,6666GROUP:EFnet n104=EFnet: EU, SE, DalarnaSERVER:irc.du.se:6666,6668,6669GROUP:EFnet n105=EFnet: EU, SE, SwedenSERVER:irc.light.se:6667GROUP:EFnet n106=EFnet: EU, UK, DemonSERVER:efnet.demon.co.uk:6665,6666,6668GROUP:EFnet n107=EFnet: EU, UK, LondonSERVER:irc.ins.net.uk:6665,6666,6668GROUP:EFnet n108=EFnet: ME, IL, InterSERVER:irc.inter.net.il:6665,6666,6668GROUP:EFnet n109=EFnet: US, CA, Los AngelesSERVER:irc.west.gblx.net:6667GROUP:EFnet n110=EFnet: US, CA, San Luis ObispoSERVER:irc.prison.net:6666,6667GROUP:EFnet n111=EFnet: US, CA, StanfordSERVER:irc.stanford.edu:6667GROUP:EFnet n112=EFnet: US, CO, ColoradoSERVER:irc.colorado.edu:5555,6665,6666GROUP:EFnet n113=EFnet: US, GA, Atlanta (Emory)SERVER:irc.emory.edu:6667GROUP:EFnet n114=EFnet: US, GA, Atlanta (Mindspring)SERVER:irc.mindspring.com:6661,6662,6663GROUP:EFnet n115=EFnet: US, IL, ChicagoSERVER:irc.mcs.net:6666,6667,6668GROUP:EFnet n116=EFnet: US, IL, ChicagoSERVER:irc.plur.net:6667GROUP:EFnet n117=EFnet: US, MI, Ann ArborSERVER:irc.umich.edu:6667GROUP:EFnet n118=EFnet: US, MN, Twin CitiesSERVER:irc.umn.edu:6665,6666,6668GROUP:EFnet n119=EFnet: US, NY, New YorkSERVER:irc.east.gblx.net:6667GROUP:EFnet n120=EgyptianIRC: Random serverSERVER:irc.egyptianirc.net:6667,6668,6669GROUP:EgyptianIRC n121=EliteOrbit: Random serverSERVER:irc.eliteorbit.net:6667GROUP:EliteOrbit n122=EntertheGame: Random serverSERVER:irc.enterthegame.com:6667,6668,6669GROUP:EntertheGame n123=Escaped: Random serverSERVER:irc.escaped.net:6662,6663,6664GROUP:Escaped n124=Esprit: Random serverSERVER:irc.esprit.net:6667GROUP:Esprit n125=euIRC: Random serverSERVER:irc.euirc.net:6665,6666,6668GROUP:euIRC n126=Exedor: Random serverSERVER:irc.exedor.net:6660,6663,6664GROUP:Exedor n127=ExodusIRC: Random serverSERVER:irc.exodusirc.net:6661,6664,6666GROUP:ExodusIRC n128=Fancynet: Random serverSERVER:irc.fancynet.com:6660,6662,6665GROUP:Fancynet n129=FastIRC: Random serverSERVER:irc.fastirc.net:6667GROUP:FastIRC n130=FDFnet: US, CA, BerkeleySERVER:transbay.fdf.net:6666,6668,9999GROUP:FDFnet n131=FDFnet: US, WA, SilverdaleSERVER:europa.fdf.net:6666,6668,6669GROUP:FDFnet n132=FDFnet: US, WI, MadisonSERVER:tower.fdf.net:6664,6665,6666GROUP:FDFnet n133=FEFnet: Random serverSERVER:irc.fef.net:6667GROUP:FEFnet n134=FEFnet: US, CA, San JoseSERVER:vendetta.fef.net:6666,8888,9999GROUP:FEFnet n135=FEFnet: US, NY, Long IslandSERVER:liii.fef.net:8888,9999GROUP:FEFnet n136=FireChat: Random serverSERVER:irc.firechat.org:6667GROUP:FireChat n137=Forestnet: Random serverSERVER:irc.forestnet.org:6667,7000GROUP:Forestnet n138=FreedomChat: Random serverSERVER:chat.freedomchat.net:6667GROUP:FreedomChat n139=Fuelienet: Random serverSERVER:irc.fuelie.net:6667,7000GROUP:Fuelienet n140=FunNet: Random serverSERVER:irc.funnet.org:6667GROUP:FunNet n141=Galaxynet: Random serverSERVER:irc.galaxynet.org:6665,6666,6668GROUP:GalaxyNet n142=Galaxynet: AU, NZ, AucklandSERVER:auckland.nz.galaxynet.org:6661,6663,6665GROUP:GalaxyNet n143=Galaxynet: EU, BE, OnlineSERVER:online.be.galaxynet.org:6663,6664,6668GROUP:GalaxyNet n144=Galaxynet: EU, SE, VltmediaSERVER:vltmedia.se.galaxynet.org:6661,6662,6668GROUP:GalaxyNet n145=Galaxynet: US, FL, FloridaSERVER:gymnet.us.galaxynet.org:6662,6663,6664GROUP:GalaxyNet n146=Galaxynet: US, WA, SeattleSERVER:freei.us.galaxynet.org:6663,6666,6668GROUP:GalaxyNet n147=Gamesnet: Random east US serverSERVER:east.gamesnet.net:6667GROUP:Gamesnet n148=Gamesnet: Random west US serverSERVER:west.gamesnet.net:6667GROUP:Gamesnet n149=Gammaforce: Random serverSERVER:irc.gammaforce.org:6663,6665,6666GROUP:Gammaforce n150=Ghostnet: Random serverSERVER:irc.ghostnet.org:6660,6662,6663GROUP:Ghostnet n151=GizNet: Random serverSERVER:irc.giznet.com:6666,6668,6669GROUP:GizNet n152=Global-irc: Random serverSERVER:irc.global-irc.net:6667GROUP:Global-irc n153=Globalchat: Random serverSERVER:irc.globalchat.org:6667GROUP:Globalchat n154=Go2Chat: China, Hong KongSERVER:irc.go2chat.net:6668,6669,7000GROUP:Go2Chat n155=Grnet: Random EU serverSERVER:gr.irc.gr:6667,7000GROUP:GRnet n156=Grnet: Random serverSERVER:srv.irc.gr:6667,7000GROUP:GRnet n157=Grnet: Random US serverSERVER:us.irc.gr:6667,7000GROUP:GRnet n158=Hellenicnet: Random serverSERVER:irc.mirc.gr:6667,7000GROUP:Hellenicnet n159=Hugsnet: Random serverSERVER:irc.hugs.net:6667,7000GROUP:Hugsnet n160=Hybnet: Random serverSERVER:irc.hybnet.net:6667GROUP:Hybnet n161=iChatZone: Random serverSERVER:irc.ichatzone.com:6667GROUP:iChatZone n162=ICQnet: Random serverSERVER:irc.icq.com:6667GROUP:ICQnet n163=Infatech: Random serverSERVER:irc.infatech.net:6660,6664,6666GROUP:Infatech n164=Infinity: Random serverSERVER:irc.infinity-irc.org:6667GROUP:Infinity n165=Insiderz: Random serverSERVER:irc.insiderz.net:6667GROUP:Insiderz n166=IRC-Hispano: Random serverSERVER:irc.irc-hispano.org:6667GROUP:IRC-Hispano n167=IRChat: Random serverSERVER:irc.irchat.net:6661,6664,6666GROUP:IRChat n168=IRChat-br: Random serverSERVER:irc.irchat.com.br:6667GROUP:IRChat-br n169=IRCLink: EU, NO, AlesundSERVER:alesund.no.eu.irclink.net:6667,6668,6669GROUP:IRCLink n170=IRCLink: EU, NO, OsloSERVER:frogn.no.eu.irclink.net:6667,6668,6669GROUP:IRCLink n171=IRCLink: US, SC, Rock HillSERVER:rockhill.sc.us.irclink.net:6667,6668,6669GROUP:IRCLink n172=IRCnet: EU, IT, RandomSERVER:irc.ircd.it:6665,6666,6668GROUP:IRCnet n173=IRCnet: AS, JP, TokyoSERVER:irc.tokyo.wide.ad.jp:6667GROUP:IRCnet n174=IRCnet: AU, MelbourneSERVER:yoyo.cc.monash.edu.au:6668,6669,9990GROUP:IRCnet n175=IRCnet: AU, SydneySERVER:irc.usyd.edu.au:6667GROUP:IRCnet n176=IRCnet: EU, AT, LinzSERVER:linz.irc.at:6666,6667,6668GROUP:IRCnet n177=IRCnet: EU, AT, WienSERVER:vienna.irc.at:6666,6668,6669GROUP:IRCnet n178=IRCnet: EU, BE, BrusselsSERVER:irc.belnet.be:6667GROUP:IRCnet n179=IRCnet: EU, BE, ZaventemSERVER:ircnet.wanadoo.be:6661,6665,6668GROUP:IRCnet n180=IRCnet: EU, CZ, PragueSERVER:irc.felk.cvut.cz:6667GROUP:IRCnet n181=IRCnet: EU, DE, BerlinSERVER:irc.fu-berlin.de:6665,6666,6668GROUP:IRCnet n182=IRCnet: EU, DE, DusseldorfSERVER:irc.freenet.de:6665,6666,6668GROUP:IRCnet n183=IRCnet: EU, DE, StuttgartSERVER:irc.belwue.de:6665,6666,6668GROUP:IRCnet n184=IRCnet: EU, DK, CopenhagenSERVER:irc.ircnet.dk:6667GROUP:IRCnet n185=IRCnet: EU, EE, TallinnSERVER:irc.estpak.ee:6666,6667,6668GROUP:IRCnet n186=IRCnet: EU, FI, EspooSERVER:irc.funet.fi:6661,6663,6665GROUP:IRCnet n187=IRCnet: EU, FI, HelsinkiSERVER:irc.cs.hut.fi:6667GROUP:IRCnet n188=IRCnet: EU, FR, NiceSERVER:irc.eurecom.fr:6667GROUP:IRCnet n189=IRCnet: EU, FR, ParisSERVER:ircnet.grolier.net:6667GROUP:IRCnet n190=IRCnet: EU, FR, PolySERVER:sil.polytechnique.fr:6667GROUP:IRCnet n191=IRCnet: EU, GR, ThessalonikiSERVER:irc.ee.auth.gr:6666,6668,6669GROUP:IRCnet n192=IRCnet: EU, HU, BudapestSERVER:irc.elte.hu:6667GROUP:IRCnet n193=IRCnet: EU, IL, HaifaSERVER:ircnet.netvision.net.il:6662,6664,6665GROUP:IRCnet n194=IRCnet: EU, IS, ReykjavikSERVER:irc.ircnet.is:6663,6664,6665GROUP:IRCnet n195=IRCnet: EU, IS, ReykjavikSERVER:irc.ircnet.is:6663,6665,6668GROUP:IRCnet n196=IRCnet: EU, IT, RomeSERVER:irc.tin.it:6665,6666,6668GROUP:IRCnet n197=IRCnet: EU, LV, RigaSERVER:irc.telia.lv:6666,6668,6669GROUP:IRCnet n198=IRCnet: EU, NL, Amsterdam (nlnet)SERVER:irc.nl.uu.net:6663,6664,6666GROUP:IRCnet n199=IRCnet: EU, NL, Amsterdam (xs4all)SERVER:irc.xs4all.nl:6661,6662,6665GROUP:IRCnet n200=IRCnet: EU, NL, EnschedeSERVER:irc.snt.utwente.nl:6660,6664,6665GROUP:IRCnet n201=IRCnet: EU, NL, NijmegenSERVER:irc.sci.kun.nl:6661,6664,6666GROUP:IRCnet n202=IRCnet: EU, NO, OsloSERVER:irc.ifi.uio.no:6667GROUP:IRCnet n203=IRCnet: EU, NO, TrondheimSERVER:irc.pvv.ntnu.no:6667GROUP:IRCnet n204=IRCnet: EU, PL, WarsawSERVER:warszawa.irc.pl:6666,6667,6668GROUP:IRCnet n205=IRCnet: EU, RU, MoscowSERVER:irc.msu.ru:6667GROUP:IRCnet n206=IRCnet: EU, SE, LuleaSERVER:irc.ludd.luth.se:6661,6663,6668GROUP:IRCnet n207=IRCnet: EU, UK, London (Btnet)SERVER:chat.bt.net:6660,6663,6666GROUP:IRCnet n208=IRCnet: EU, UK, London (Demon)SERVER:ircnet.demon.co.uk:6665,6666,6668GROUP:IRCnet n209=IRCnet: EU, UK, London (Netcom)SERVER:irc.netcom.net.uk:6660,6661,6662GROUP:IRCnet n210=IRCnet: EU, UK, Warrington (u-net)SERVER:irc.u-net.com:6662,6665,6666GROUP:IRCnet n211=IRCnet: US, NY, New YorkSERVER:irc.stealth.net:6661,6663,6665GROUP:IRCnet n212=IRCStorm: Random serverSERVER:irc.ircstorm.net:6667GROUP:IRCStorm n213=IrcTalk: Random serverSERVER:irc.irctalk.net:6667GROUP:IrcTalk n214=IRCworld: Random serverSERVER:irc.ircworld.org:6667GROUP:IRCworld n215=Israelnet: Random serverSERVER:irc.israel.net:6667GROUP:Israelnet n216=K0wNet: Random serverSERVER:irc.k0w.net:6660,6661,6664GROUP:K0wNet n217=Kewl.org: Random serverSERVER:irc.kewl.org:6667GROUP:KewlOrg n218=Kewl.org: EU, FR, NanterreSERVER:nanterre.fr.eu.kewl.org:6667GROUP:KewlOrg n219=Kewl.org: EU, UK, LondonSERVER:london.uk.eu.kewl.org:6667GROUP:KewlOrg n220=Kidsworld: EU, FR, ParisSERVER:paris.fr.eu.kidsworld.org:6661,6662,6666GROUP:KidsWorld n221=Kidsworld: US, CO, DenverSERVER:denver.co.us.kidsworld.org:6666,6668,6669GROUP:KidsWorld n222=Kidsworld: US, MD, BaltimoreSERVER:baltimore.md.us.kidsworld.org:6666,6668,6669GROUP:KidsWorld n223=KissLand: Random serverSERVER:irc.kissland.com:6667GROUP:KissLand n224=Knightnet: AF, ZA, DurbanSERVER:orc.dbn.za.knightnet.net:6667,5555GROUP:Knightnet n225=Knightnet: US, CA, GoldengateSERVER:goldengate.ca.us.knightnet.net:6667,5555GROUP:Knightnet n226=KreyNet: Random serverSERVER:irc.krey.net:6667GROUP:Kreynet n227=Krushnet: Random serverSERVER:irc.krushnet.org:6667GROUP:Krushnet n228=LagNet: Random serverSERVER:irc.lagnet.org.za:6667GROUP:LagNet n229=LagNet: AF, ZA, Cape TownSERVER:reaper.lagnet.org.za:6667GROUP:LagNet n230=LagNet: AF, ZA, JohannesburgSERVER:mystery.lagnet.org.za:6667GROUP:LagNet n231=Librenet: Random serverSERVER:irc.librenet.net:6667GROUP:Librenet n232=Lunatics: Random serverSERVER:irc.lunatics.net:6667,6668,6669GROUP:Lunatics n233=MagicStar: Random serverSERVER:irc.magicstar.net:6667GROUP:MagicStar n234=MavraNet: Random serverSERVER:irc.mavra.net:6663,6664,6668GROUP:MavraNet n235=MediaDriven: Random serverSERVER:irc.mediadriven.com:6667,6668,6669GROUP:MediaDriven n236=Messique: Random serverSERVER:irc.messique.org:6663,6664,6665GROUP:Messique n237=Millenia: Random serverSERVER:irc.millenia.org:6664,6668,6669GROUP:Millenia n238=mIRCnet: Random serverSERVER:irc.mirc.net:6667GROUP:mIRCnet n239=Mozilla: Random serverSERVER:irc.mozilla.org:6667,7000GROUP:Mozilla n240=Muhabbet: Random serverSERVER:irc.muhabbet.net:6667,8888,9000GROUP:Muhabbet n241=Mysteria: Random serverSERVER:irc.mysteria.net:6667,7000GROUP:Mysteria n242=Mystical: Random serverSERVER:irc.mystical.net:6667,7000GROUP:Mystical n243=NdrsNet: Random serverSERVER:irc.ndrsnet.com:6661,6663,6665GROUP:NdrsNet n244=Nebulanet: Random serverSERVER:irc.nebulanet.org:6661,6663,6668GROUP:Nebulanet n245=Net-France: Random serverSERVER:irc.net-france.com:6667GROUP:Net-France n246=Nevernet: Random serverSERVER:irc.nevernet.net:6667GROUP:Nevernet n247=Newnet: Random serverSERVER:irc.newnet.net:6665,6666,6667GROUP:Newnet n248=Newnet: EU, DE, TrustedSERVER:irc.trusted-network.de:6666,7000GROUP:Newnet n249=Newnet: EU, UK, OasisSERVER:irc.oasis-net.net:6666,7000GROUP:Newnet n250=Newnet: NZ, AucklandSERVER:irc.uplink.net.nz:6666,6668,6669GROUP:Newnet n251=Newnet: US, CA, FlagglerockSERVER:irc.fragglerock.org:6666,7000GROUP:Newnet n252=Newnet: US, MA, ChelmsfordSERVER:irc.chelmsford.com:6663,6664,6666GROUP:Newnet n253=Newnet: US, VA, RandolphSERVER:irc.rma.edu:6660,6661,6669GROUP:Newnet n254=Nightstar: Random serverSERVER:irc.nightstar.net:6665,6666,6668GROUP:NightStar n255=Nitro: Random serverSERVER:irc.nitroirc.net:6667GROUP:Nitro n256=Novernet: Random serverSERVER:irc.novernet.com:6665,6668,6669GROUP:Novernet n257=Novernet: US, GA, AugustaSERVER:irc.c-plusnet.com:6665,6666,6668GROUP:Novernet n258=Novernet: US, MN, MinneapolisSERVER:chat.novernet.com:6667GROUP:Novernet n259=OpenMind: Random serverSERVER:irc.openmind.net:6667GROUP:OpenMind n260=Opennet: Random serverSERVER:irc.opennetirc.org:6667GROUP:Opennet n261=Othernet: Random serverSERVER:irc.othernet.org:6667GROUP:Othernet n262=Othernet: US, FL, MiamiSERVER:miami.fl.us.othernet.org:6667GROUP:Othernet n263=Othernet: US, MO, StLouisSERVER:stlouis.mo.us.othernet.org:6667GROUP:Othernet n264=Otherside: Random serverSERVER:irc.otherside.com:6667GROUP:OtherSide n265=Outsiderz: Random serverSERVER:irc.outsiderz.com:6667GROUP:Outsiderz n266=OzChat: Random serverSERVER:irc.ozchat.org:6667GROUP:OzChat n267=OzOrg: AU, AdelaideSERVER:chariot.adelaide.oz.org:6666,6668,7000GROUP:OzOrg n268=OzOrg: AU, PerthSERVER:iinet.perth.oz.org:6667GROUP:OzOrg n269=OzOrg: AU, SydneySERVER:aussie.sydney.oz.org:6668,6669,7000GROUP:OzOrg n270=OzTic: Random serverSERVER:irc.oztic.net:6668,6669,7000GROUP:OzTic n271=Phishynet: Random serverSERVER:irc.phishy.net:6667,7000GROUP:Phishynet n272=Pinoycentral: Random serverSERVER:chat.abs-cbn.com:6667GROUP:Pinoycentral n273=Planetarion: Random serverSERVER:irc.planetarion.com:6667GROUP:Planetarion n274=PowerChat: Random serverSERVER:irc.powerchat.org:6729,6793,6997GROUP:PowerChat n275=Pseudonet: Random serverSERVER:irc.pseudonet.org:6667GROUP:Pseudonet n276=PTlink: Random serverSERVER:irc.ptlink.net:6667GROUP:PTlink n277=PTnet: EU, PT, FaroSERVER:ualg.ptnet.org:6667GROUP:PTnet n278=PTnet: EU, PT, LisboaSERVER:telepac2.ptnet.org:6667GROUP:PTnet n279=QChat: Random serverSERVER:irc.qchat.net:6667GROUP:QChat n280=QuakeNet: Random serverSERVER:irc.quakenet.eu.org:6667,6668,6669GROUP:QuakeNet n281=Raptanet: Random serverSERVER:irc.rapta.net:6667GROUP:Raptanet n282=Raptornet: Random serverSERVER:irc.raptornet.org:6667GROUP:Raptornet n283=Rebelchat: Random serverSERVER:irc.rebelchat.org:6667GROUP:Rebelchat n284=Red-Latina: Random serverSERVER:irc.red-latina.org:6667GROUP:Red-Latina n285=Red-Latina: NA, MX, SanJoseSERVER:irc.dalsom.net:6666,6667GROUP:Red-Latina n286=RedeBrasil: BR, DF, BrasiliaSERVER:irc.persocom.com.br:6668,6669,7001GROUP:RedeBrasil n287=RedeBrasil: BR, PE, CaruaruSERVER:irc.netstage.com.br:6669,7000,7001GROUP:RedeBrasil n288=RedeBrasil: BR, PE, RecifeSERVER:irc.elogica.com.br:6669,7000,7001GROUP:RedeBrasil n289=RedeSul: BR, PR, MaringaSERVER:irc.wnet.com.br:6667GROUP:RedeSul n290=RedeSul: BR, SC, BlumenauSERVER:irc.braznet.com.br:6667,9001GROUP:RedeSul n291=RedLatona: Random serverSERVER:irc.redlatona.net:6667,6668GROUP:RedLatona n292=RekorNet: Random serverSERVER:irc.rekor.net:6664,6666,6669GROUP:RekorNet n293=Relicnet: Random serverSERVER:irc.relic.net:6667GROUP:Relicnet n294=Relicnet: US, MA, CycloneSERVER:cyclone.us.relic.net:6661,6663,6664GROUP:Relicnet n295=Risanet: Random serverSERVER:irc.risanet.com:6667,6668,6669GROUP:Risanet n296=Rootshell: Random serverSERVER:irc.rootshell.za.org:6667GROUP:Rootshell n297=Rusnet: EU, RU, TomskSERVER:irc.tsk.ru:6668,7770,7772GROUP:Rusnet n298=Rusnet: EU, RU, VladivostokSERVER:irc.vladivostok.ru:7772,7773,7774GROUP:Rusnet n299=Rusnet: EU, UA, KievSERVER:irc.kar.net:7772,7773,7774GROUP:Rusnet n300=SamShark: Random serverSERVER:irc.samshark.com:6667GROUP:SamShark n301=Sandnet: Random serverSERVER:irc.sandnet.net:6660,6663,6664GROUP:Sandnet n302=Sandnet: US, CA, MysticSERVER:mystic.tides.sandnet.net:6662,6663,6664GROUP:Sandnet n303=Sandnet: US, NJ, DuneBuggySERVER:dunebuggy.nj.sandnet.net:6661,6664,6665GROUP:Sandnet n304=Seveneagle: Random serverSERVER:irc.seveneagle.net:6667GROUP:Seveneagle n305=SexNet: Random serverSERVER:irc.sexnet.org:6667GROUP:SexNet n306=SgNet: Random serverSERVER:irc.sgnet.org:6660,6663,6665GROUP:SgNet n307=ShadowFire: Random serverSERVER:irc.shadowfire.org:6667GROUP:ShadowFire n308=ShadowWorld: Random serverSERVER:irc.shadowworld.net:6667GROUP:shadowWorld n309=SideNet: Random serverSERVER:irc.sidenet.org:6751,6799,6803GROUP:SideNet n310=Skyyenet: US, VA, ArlingtonSERVER:arlington.va.us.skyyenet.org:6667GROUP:Skyyenet n311=Slashnet: Random serverSERVER:irc.slashnet.org:6667GROUP:Slashnet n312=Solarchat: Random serverSERVER:irc.solarchat.net:6667,7000GROUP:Solarchat n313=Sorcerynet: Random serverSERVER:irc.sorcery.net:6667,7000,9000GROUP:Sorcery n314=Sorcerynet: EU, SE, KarlskronaSERVER:nexus.sorcery.net:6667,7000,9000GROUP:Sorcery n315=Sorcerynet: US, CA, Palo AltoSERVER:kechara.sorcery.net:6667,7000,9000GROUP:Sorcery n316=Spamnet: Random serverSERVER:irc.spamnet.org:6667,6668,6669GROUP:Spamnet n317=StarChat: Random serverSERVER:irc.starchat.net:6668,6669,7000GROUP:StarChat n318=StarChat: AU, QLD, SouthernCrossSERVER:boomer.qld.au.starchat.net:6668,6669,7000GROUP:StarChat n319=StarChat: EU, NO, AskerSERVER:reality.no.eu.starchat.net:6668,6669,7000GROUP:StarChat n320=StarChat: US, CA, San JoseSERVER:sand.ca.us.starchat.net:6668,6669,7000GROUP:StarChat n321=StarLink-irc: Random serverSERVER:irc.starlink-irc.org:6667GROUP:starlink-irc n322=StarLink-irc: US, MI, RochesterSERVER:rochester.mi.us.starlink-irc.org:6667GROUP:Starlink-irc n323=StarLink-irc: US, TX, HoustonSERVER:houston.tx.us.starlink-irc.org:6667GROUP:Starlink-irc n324=StarLink Org: US, CO, DenverSERVER:denver.co.us.starlink.org:6661,6662,6666GROUP:StarlinkOrg n325=StarLink Org: US, NC, DurhamSERVER:durham-r.nc.us.starlink.org:6660,6662,6663GROUP:StarlinkOrg n326=StarLink Org: US, TX, WichitaFallsSERVER:wichitafalls.tx.us.starlink.org:6666,6667,6668GROUP:StarlinkOrg n327=StarWars-IRC: Random serverSERVER:irc.starwars-irc.net:6663,6664,6665GROUP:StarWars-IRC n328=Stormdancing: Random serverSERVER:irc.stormdancing.net:6666,6668,6669GROUP:Stormdancing n329=Styliso: Random serverSERVER:irc.styliso.net:6667GROUP:Styliso n330=Sub-city: Random serverSERVER:irc.sub-city.net:6668,6669,7000GROUP:Sub-city n331=Superchat: Random serverSERVER:irc.superchat.org:6661,6663,6664GROUP:Superchat n332=Superonline: Random serverSERVER:irc.superonline.com:6665,6668,6669GROUP:Superonline n333=Sysopnet: Random serverSERVER:irc.sysopnet.org:6666,6667,6668GROUP:Sysopnet n334=Techdreams: Random serverSERVER:irc.techdreams.net:6667GROUP:Techdreams n335=Telstra: Random serverSERVER:irc.telstra.com:6667,6668,6669GROUP:Telstra n336=Thunderirc: Random serverSERVER:irc.thunderirc.net:6667GROUP:Thunderirc n337=TR-net: EU, TR, AnkaraSERVER:irc.dominet.com.tr:6667GROUP:TR-net n338=TR-net: EU, Tr, IstanbulSERVER:irc.teklan.com.tr:6667GROUP:TR-net n339=TRcom: Random serverSERVER:irc.trcom.net:6666,6668,6669GROUP:TRcom n340=Treklink: Random serverSERVER:irc.treklink.net:6667GROUP:Treklink n341=UltraIRC: Random serverSERVER:irc.ultrairc.net:6667GROUP:UltraIRC n342=Undernet: CA, ON, TorontoSERVER:toronto.on.ca.undernet.org:6661,6664,6665GROUP:Undernet n343=Undernet: CA, QC, MontrealSERVER:montreal.qu.ca.undernet.org:6662,6664,6665GROUP:Undernet n344=Undernet: EU, AT, GrazSERVER:graz.at.eu.undernet.org:6661,6662,6666GROUP:Undernet n345=Undernet: EU, BE, AntwerpSERVER:flanders.be.eu.undernet.org:6660,6663,6668GROUP:Undernet n346=Undernet: EU, BE, BrusselsSERVER:brussels.be.eu.undernet.org:6660,6662,6664GROUP:Undernet n347=Undernet: EU, FR, CaenSERVER:caen.fr.eu.undernet.org:6666,6668,6669GROUP:undernet n348=Undernet: EU, FR, ParisSERVER:paris.fr.eu.undernet.org:6664,6665,6666GROUP:Undernet n349=Undernet: EU, NL, DiemenSERVER:diemen.nl.eu.undernet.org:6662,6663,6664GROUP:Undernet n350=Undernet: EU, NL, HaarlemSERVER:haarlem.nl.eu.undernet.org:6660,6663,7000GROUP:Undernet n351=Undernet: EU, NO, OsloSERVER:oslo.no.eu.undernet.org:6663,6664,6666GROUP:Undernet n352=Undernet: EU, SE, GothenburgSERVER:gothenburg.se.eu.undernet.org:6661,6666,6668GROUP:Undernet n353=Undernet: EU, UK, LondonSERVER:london.uk.eu.undernet.org:6666,6668,6669GROUP:Undernet n354=Undernet: NZ, AucklandSERVER:auckland.nz.undernet.org:6667,6668GROUP:Undernet n355=Undernet: US, AZ, PhoenixSERVER:phoenix.az.us.undernet.org:6664,6665,6668GROUP:Undernet n356=Undernet: US, CA, Los-AngelesSERVER:los-angeles.ca.us.undernet.org:6660,6661,6664GROUP:Undernet n357=Undernet: US, CA, San DiegoSERVER:sandiego.ca.us.undernet.org:6662,6664,6665GROUP:Undernet n358=Undernet: US, DC, WashingtonSERVER:washington.dc.us.undernet.org:6660,6661,6665GROUP:Undernet n359=Undernet: US, GA, AtlantaSERVER:atlanta.ga.us.undernet.org:6662,6663,6668GROUP:Undernet n360=Undernet: US, KS, ManhattanSERVER:manhattan.ks.us.undernet.org:6661,6665,6668GROUP:Undernet n361=Undernet: US, MD, BaltimoreSERVER:baltimore.md.us.undernet.org:6665,6666,6668GROUP:Undernet n362=Undernet: US, MI, Ann-arborSERVER:ann-arbor.mi.us.undernet.org:6663,6664,6665GROUP:Undernet n363=Undernet: US, NV, Las VegasSERVER:lasvegas.nv.us.undernet.org:6660,6664,6666GROUP:Undernet n364=Undernet: US, NY, NewYorkSERVER:newyork.ny.us.undernet.org:6660,6663,6666GROUP:Undernet n365=Undernet: US, TX, DallasSERVER:dallas.tx.us.undernet.org:6661,6662,6663GROUP:Undernet n366=Undernet: US, UT, SaltlakeSERVER:saltlake.ut.us.undernet.org:6660,6661,6666GROUP:Undernet n367=Undernet: US, VA, ArlingtonSERVER:arlington.va.us.undernet.org:6662,6664,6665GROUP:Undernet n368=Undernet: US, VA, McLeanSERVER:mclean.va.us.undernet.org:6666,6668,6669GROUP:Undernet n369=UnderZ: Random serverSERVER:irc.underz.org:6667,6668GROUP:UnderZ n370=Undienet: Random serverSERVER:irc.undienet.org:6662,6663,6664GROUP:Undienet n371=Unfnet: Random serverSERVER:irc.unfnet.net:6668,6669,7000GROUP:Unfnet n372=UnionLatina: Random serverSERVER:irc.unionlatina.org:6667GROUP:UnionLatina n373=UnitedChat Net: Random serverSERVER:irc.unitedchat.net:6667GROUP:UnitedchatNet n374=UnitedChat Org: Random serverSERVER:irc.unitedchat.org:6667GROUP:UnitedchatOrg n375=Univers: Random serverSERVER:irc.univers.org:6665,6666,6668GROUP:Univers n376=Vidgamechat: Random serverSERVER:irc.vidgamechat.com:6667GROUP:Vidgamechat n377=Virtuanet: Random serverSERVER:irc.virtuanet.org:6661,6663,6666GROUP:Virtuanet n378=Vitamina: Random serverSERVER:irc-rr.vitamina.ca:6667GROUP:Vitamina n379=Warpednet: Random serverSERVER:irc.warped.net:6667GROUP:Warpednet n380=Webnet: Random serverSERVER:irc.webchat.org:6668,6669,7000GROUP:Webnet n381=Webnet: US, AL, DothanSERVER:wiregrass.al.us.webchat.org:6668,6669,7000GROUP:Webnet n382=Webnet: US, CA, Santa ClaraSERVER:webmaster.ca.us.webchat.org:6662,6665,6668GROUP:Webnet n383=Webnet: US, MA, BostonSERVER:greennet.ma.us.webchat.org:6668,6669,7000GROUP:Webnet n384=Wolfpac: Random serverSERVER:irc.wolfpac.org:6662,6664,6668GROUP:Wolfpac n385=WonKnet: Random serverSERVER:irc.wonk.net:6668,6669,7000GROUP:Wonknet n386=Woot: Random serverSERVER:irc.woot.net:6667GROUP:Woot n387=WorldIRC: Random serverSERVER:irc.worldirc.org:6660,6662,6663GROUP:WorldIRC n388=Xevion: Random serverSERVER:irc.xevion.net:6667,7000GROUP:Xevion n389=Xnet: Random AU serverSERVER:au.xnet.org:6667GROUP:Xnet n390=Xnet: Random EU serverSERVER:eu.xnet.org:6667GROUP:Xnet n391=Xnet: Random US serverSERVER:us.xnet.org:6667GROUP:Xnet n392=XWorld: Random serverSERVER:irc.xworld.org:6667GROUP:XWorld n393=ZAnet Net: AF, ZA, CI (lia)SERVER:lia.zanet.net:6667GROUP:ZAnetNet n394=ZAnet Net: AF, ZA, MWeb (timewiz)SERVER:timewiz.zanet.net:6667GROUP:ZAnetNet n395=ZAnet Org: AF, ZA, Cape Town (gaspode)SERVER:gaspode.zanet.org.za:6667GROUP:ZAnetOrg n396=ZAnet Org: AF, ZA, Johannesburg (is)SERVER:is.zanet.org.za:6667GROUP:ZAnetOrg n397=ZAnet Org: AF, ZA, Midrand (ethereal)SERVER:ethereal.zanet.org.za:6660,6666GROUP:ZAnetOrg n398=ZiRC: Random serverSERVER:irc.zirc.org:6661,6666,6668GROUP:ZiRC n399=ZUHnet: Random serverSERVER:irc.zuh.net:6667GROUP:ZUHnet n400=Zurna: Random serverSERVER:irc.zurna.net:6667GROUP:Zurna '! ! !IRCServer class methodsFor: 'test' stamp: 'sbw 2/26/2002 18:55'! populateServersDatabaseWithBuiltIn "IRCServer populateServersDatabaseWithBuiltIn" "Test populate from internal information." | stream | stream _ ReadStream on: self mircServersIni. IRCConnectionEnhanced initializeServers. IRCConnectionEnhanced buildServersListFrommIRCIniStream: stream reset. self addOpenProjectsNetServer! ! !IRCServer class methodsFor: 'test' stamp: 'sbw 2/24/2002 21:08'! test "IRCServer test" "Test populate from an external file." | path | path _ 'servers.ini'. IRCConnectionEnhanced initializeServers. IRCConnectionEnhanced buildServersListFrommIRCIniFilePath: path. IRCConnectionEnhanced serversList inspect! ! !IRCServer class methodsFor: 'test' stamp: 'sbw 2/24/2002 21:11'! test2 "IRCServer test2" "Test populate from internal information." | stream | stream _ (ReadStream on: self mircServersIni). IRCConnectionEnhanced initializeServers. IRCConnectionEnhanced buildServersListFrommIRCIniStream: stream. IRCConnectionEnhanced serversList inspect! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:08'! addActionButtonsTo: parentMorph fractions: fractions offsets: offsets | buttons alignMorph btn | 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)). parentMorph addMorph: alignMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [alignMorph borderWidth: 2] ifFalse: [alignMorph borderWidth: 0]. btn _ self findMorphNamed: 'okayButton' in: buttons. btn isNil ifFalse: [btn offColor: Color veryVeryLightGray; setBalloonText: self okayButtonInactiveHelpText]! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 11/16/2002 12:16'! addGroupListTo: parentMorph fractions: fractions offsets: offsets | alignMorph topOffset listMorph labelMorph | topOffset _ 12. alignMorph _ AlignmentMorph newRow hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 2; color: Color paleTan; layoutPolicy: ProportionalLayout new. labelMorph _ TextMorph new. labelMorph contents: 'Group' asText allBold; backgroundColor: Color transparent. listMorph _ PluggableListMorph on: self list: #groupList selected: #selectedGroupIndex changeSelected: #selectedGroupIndex: menu: nil. alignMorph addMorph: labelMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 0) offsets: (6 @ -2 corner: 0 @ topOffset)). alignMorph addMorph: listMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ topOffset corner: 0 @ 0)). parentMorph addMorph: alignMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [alignMorph borderWidth: 2] ifFalse: [alignMorph borderWidth: 0]! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:23'! addPortListTo: parentMorph fractions: fractions offsets: offsets | alignMorph topOffset listMorph labelMorph | topOffset _ 12. alignMorph _ AlignmentMorph newRow hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 2; layoutPolicy: ProportionalLayout new. labelMorph _ TextMorph new. labelMorph contents: 'Port' asText allBold. listMorph _ PluggableListMorph on: self list: #portList selected: #selectedPortIndex changeSelected: #selectedPortIndex: menu: nil. alignMorph addMorph: labelMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 0) offsets: (6 @ -2 corner: 0 @ topOffset)). alignMorph addMorph: listMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ topOffset corner: 0 @ 0)). parentMorph addMorph: alignMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [alignMorph borderWidth: 2] ifFalse: [alignMorph borderWidth: 0]! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 08:53'! addServerAddressTo: parentMorph fractions: fractions offsets: offsets | alignMorph labelMorph addressMorph | alignMorph _ AlignmentMorph newRow hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 2; layoutPolicy: ProportionalLayout new. labelMorph _ TextMorph new. labelMorph contents: 'Server Address: ' asText allBold; lock. addressMorph _ TextMorph new. addressMorph contents: self noServerAddress asText; setNameTo: 'serverAddress'. alignMorph addMorph: labelMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 0 @ 1) offsets: (6 @ 2 corner: 100 @ 0)). alignMorph addMorph: addressMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (100 @ 2 corner: 0 @ 0)). parentMorph addMorph: alignMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [alignMorph borderWidth: 2] ifFalse: [alignMorph borderWidth: 0]! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:59'! addServerListTo: parentMorph fractions: fractions offsets: offsets | alignMorph topOffset listMorph labelMorph | topOffset _ 12. alignMorph _ AlignmentMorph newRow hResizing: #spaceFill; vResizing: #spaceFill; layoutInset: 0; borderWidth: 2; layoutPolicy: ProportionalLayout new. labelMorph _ TextMorph new. labelMorph contents: 'Server' asText allBold. listMorph _ PluggableListMorph on: self list: #serverList selected: #selectedServerIndex changeSelected: #selectedServerIndex: menu: nil. alignMorph addMorph: labelMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 0) offsets: (6 @ -2 corner: 0 @ topOffset)). alignMorph addMorph: listMorph fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ topOffset corner: 0 @ 0)). parentMorph addMorph: alignMorph fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [alignMorph borderWidth: 2] ifFalse: [alignMorph borderWidth: 0]! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 21:42'! buildDialog | window | window _ (SystemWindow labelled: 'IRC Groups and Servers') model: self. self buildMorphIn: window. ^ window! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 21:55'! buildMorphIn: parentMorph "IRCServerDialog test" | buttonHeight addressHeight | buttonHeight _ 26. addressHeight _ 26. self addGroupListTo: parentMorph fractions: (0 @ 0 corner: 0.4 @ 1) offsets: (0 @ 0 corner: 0 @ (buttonHeight + addressHeight) negated); addServerListTo: parentMorph fractions: (0.4 @ 0 corner: 0.8 @ 1) offsets: (0 @ 0 corner: 0 @ (buttonHeight + addressHeight) negated); addPortListTo: parentMorph fractions: (0.8 @ 0 corner: 1 @ 1) offsets: (0 @ 0 corner: 0 @ (buttonHeight + addressHeight) negated); addServerAddressTo: parentMorph fractions: (0 @ 1 corner: 1 @ 1) offsets: (0 @ (buttonHeight + addressHeight) negated corner: 0 @ buttonHeight negated); addActionButtonsTo: parentMorph fractions: (0 @ 1 corner: 1 @ 1) offsets: (0 @ buttonHeight negated corner: 0 @ 0)! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:00'! buttonRow | aRow btn | aRow _ AlignmentMorph newRow beSticky. aRow color: Color transparent. aRow clipSubmorphs: true. aRow layoutInset: 10 @ 1; cellInset: 14. self buttonSpecs do: [:spec | btn _ PluggableButtonMorph on: self getState: nil action: spec second. btn color: Color transparent; hResizing: #spaceFill; vResizing: #spaceFill; useRoundedCorners; setNameTo: spec third; label: spec first asString. btn onColor: Color veryVeryLightGray offColor: Color transparent. aRow addMorphBack: btn. btn setBalloonText: spec fourth]. ^ aRow! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:06'! buttonSpecs "button label, selector, button name, help text" ^ #(#('Okay' #serverSelected 'okayButton' '') #('Cancel' #cancelSelected 'cancelButton' 'Discard values') )! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:05'! findMorphNamed: aString ^ self findMorphNamed: aString in: self topView ! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:04'! findMorphNamed: aString in: parentMorph parentMorph isNil ifTrue: [^ nil]. ^ parentMorph submorphNamed: aString ifNone: []! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:26'! groupList ^ self serversDict keys asSortedCollection! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:04'! initialExtent ^ 500 @ 400! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:07'! initialize "Place holder."! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:18'! initializeServersDict "Generated - Private. Initialize the value of serversDict." self serversDict: Dictionary new! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:17'! noServerAddress ^ 'none specified'! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:06'! okayButtonActiveHelpText ^'Accept the current values for your IRC server connection'! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:07'! okayButtonInactiveHelpText ^ 'Currently disabled until all server parameters are selected'! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 21:40'! openDialog ^ self buildDialog openInWorld! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:42'! parentDialog: morph parentDialog _ morph! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:28'! portList | server | self selectedGroupIndex = 0 ifTrue: [^ #()]. self selectedServerIndex = 0 ifTrue: [^ #()]. server _ self selectedServer. ^ server ports collect: [:x | x asString]! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:43'! selectedGroup selectedGroupIndex = 0 ifTrue: [^nil]. ^ self groupList at: self selectedGroupIndex! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:43'! selectedGroupIndex selectedGroupIndex == nil ifTrue: [selectedGroupIndex _ 0]. ^ selectedGroupIndex! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:21'! selectedGroupIndex: index selectedGroupIndex _ index. selectedServerIndex _ 0. selectedPortIndex _ 0. self changed: #selectedGroupIndex. self changed: #serverList. self changed: #portList. self updateServerAddressDisplay! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:31'! selectedPort | server | self selectedGroupIndex = 0 ifTrue: [^ nil]. self selectedServerIndex = 0 ifTrue: [^ nil]. self selectedPortIndex = 0 ifTrue: [^nil]. server _ self selectedServer. ^ server ports at: self selectedPortIndex! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:20'! selectedPortIndex selectedPortIndex == nil ifTrue: [selectedPortIndex _ 0]. ^ selectedPortIndex! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:33'! selectedPortIndex: index selectedPortIndex _ index. self changed: #selectedPortIndex. self updateServerAddressDisplay! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:13'! selectedServer | servers | servers _ self serversDict at: self selectedGroup ifAbsent: [^nil]. self selectedServerIndex = 0 ifTrue: [^nil]. ^servers at: self selectedServerIndex. ! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:33'! selectedServerAddress | server port | server _ self selectedServer. server isNil ifTrue: [^ self noServerAddress]. port _ self selectedPort. ^port isNil ifTrue: [server address] ifFalse: [server address,':', port printString]! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:43'! selectedServerIndex selectedServerIndex == nil ifTrue: [selectedServerIndex _ 0]. ^ selectedServerIndex! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:22'! selectedServerIndex: index selectedServerIndex _ index. selectedPortIndex _ 0. self changed: #selectedServerIndex. self changed: #portList. self updateServerAddressDisplay ! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:09'! selectedServerName selectedServerIndex = 0 ifTrue: [^ nil]. ^ self serverList at: self selectedServerIndex! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:40'! serverList | group servers | self selectedGroupIndex = 0 ifTrue: [^ #()]. group _ self selectedGroup. servers _ self serversDict at: group. ^ servers collect: [:x | x serverName]! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:18'! serversDict "Generated - Return the value of serversDict." serversDict == nil ifTrue: [self initializeServersDict]. ^ serversDict! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:18'! serversDict: anObject "Generated - Set the value of serversDict to ." serversDict _ anObject! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:11'! updateOkayButtonState | btn | btn _ self findMorphNamed: 'okayButton'. btn isNil ifFalse: [ self validServerSelection ifTrue: [btn offColor: Color transparent; setBalloonText: self okayButtonActiveHelpText] ifFalse: [btn offColor: Color veryVeryLightGray; setBalloonText: self okayButtonInactiveHelpText]] ! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 08:18'! updateServerAddressDisplay | morph | morph _ self findMorphNamed: 'serverAddress'. morph isNil ifTrue: [^nil]. morph contents: self selectedServerAddress. self updateOkayButtonState! ! !IRCServerDialog methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 23:47'! validServerSelection self selectedServer isNil ifTrue: [^false]. ^self selectedPort isNil not! ! !IRCServerDialog methodsFor: 'exit management' stamp: 'sbw 3/3/2002 21:55'! cancelSelected "Make the server selection invalid and close our window." selectedServerIndex _ 0. self topView delete! ! !IRCServerDialog methodsFor: 'exit management' stamp: 'sbw 3/3/2002 21:56'! serverSelected "Okay button pressed. If our selection is valid, close the window and tell our caller." self validServerSelection ifTrue: [self topView delete. parentDialog isNil ifFalse: [parentDialog useValuesFromServer: self selectedServer port: self selectedPort]]! ! !IRCServerDialog methodsFor: 'exit management' stamp: 'sbw 3/3/2002 21:58'! windowIsClosing "If we got here from a #serverSelected, the caller already knows. If not, then it was either the cancel button or the window close button. In that case, tell the caller." self validServerSelection ifTrue: [^ nil]. parentDialog isNil ifTrue: [^ nil]. parentDialog noServerSelected! ! !IRCServerDialog class methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:05'! new ^ super new initialize! ! !IRCServerDialog class methodsFor: 'as yet unclassified' stamp: 'sbw 2/25/2002 00:30'! on: list ^self new serversDict: list! ! !IRCServerDialog class methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 22:24'! test "IRCServerDialog test" self new serversDict: IRCConnectionEnhanced serversListByGroups; openDialog! ! !Interval methodsFor: 'accessing' stamp: 'sbw 3/1/2002 13:03'! end ^stop! ! !Morph methodsFor: 'submorphs-accessing' stamp: 'sbw 2/21/2002 23:52'! decrementIndexPositionOfSubmorph: morph | indexOfMorph indexOfPartner temp | submorphs isSequenceable ifFalse: [^ nil]. indexOfMorph _ submorphs indexOf: morph. indexOfMorph = 1 ifTrue: [^nil]. indexOfPartner _ indexOfMorph - 1. temp _ submorphs at: indexOfPartner. self invalidRect: self fullBounds. submorphs at: indexOfPartner put: morph. submorphs at: indexOfMorph put: temp. self layoutChanged! ! !Morph methodsFor: 'submorphs-accessing' stamp: 'sbw 2/21/2002 23:52'! incrementIndexPositionOfSubmorph: morph | indexOfMorph indexOfPartner temp | submorphs isSequenceable ifFalse: [^ nil]. indexOfMorph _ submorphs indexOf: morph. indexOfMorph = submorphs size ifTrue: [^ nil]. indexOfPartner _ indexOfMorph + 1. temp _ submorphs at: indexOfPartner. self invalidRect: self fullBounds. submorphs at: indexOfPartner put: morph. submorphs at: indexOfMorph put: temp. self layoutChanged! ! !IRCMorph methodsFor: 'initialization' stamp: 'sbw 2/19/2002 13:05'! superInitialize "This will be removed, as well as the sender, once we integrate." ^ super initialize! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:23'! addButtons: fractions plus: offsets | column | column _ AlignmentMorph newColumn. self addConnectButtonTo: column. self addMOTDButtonTo: column. self addChannelListButtonTo: column. self addMorph: column fullFrame: (LayoutFrame fractions: fractions offsets: offsets). Preferences alternativeWindowLook ifTrue: [column borderWidth: 2] ifFalse: [column borderWidth: 0]! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:14'! addChannelListButtonTo: column | channelListButton | channelListButton _ PluggableButtonMorph on: self getState: nil action: #openChannelList. channelListButton hResizing: #spaceFill; vResizing: #spaceFill; label: 'channel list'. column addMorphBack: channelListButton. ! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:12'! addConnectButtonTo: column | connectButton | connectButton _ PluggableButtonMorph on: self getState: nil action: #openConnection. connectButton hResizing: #spaceFill; vResizing: #spaceFill; label: 'connect to server'. column addMorphBack: connectButton. ! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:29'! addConsoleText: fractions plus: offsets | consoleView | consoleText _ Text new. consoleView _ PluggableTextMorph on: self text: #consoleText accept: nil readSelection: #consoleTextSelection menu: nil. self addMorph: consoleView fullFrame: (LayoutFrame fractions: fractions offsets: offsets). consoleView color: (Color r: 0.937 g: 0.937 b: 0.937)! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:31'! addInputPane: fractions plus: offsets | inputPane | inputPane _ PluggableTextMorph on: self text: nil accept: #sendCommand:. self addMorph: inputPane fullFrame: (LayoutFrame fractions: fractions offsets: offsets). ! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:13'! addMOTDButtonTo: column | motdButton | motdButton _ PluggableButtonMorph on: self getState: nil action: #openMotd. motdButton hResizing: #spaceFill; vResizing: #spaceFill; label: 'view MOTD'. column addMorphBack: motdButton. ! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:02'! connect connection ifNotNil: [connection disconnect]. connection ifNil: [connection _ IRCConnectionEnhanced new]. connection connectToServer: server port: port nick: nick user: userName fullName: fullName. connection subscribeToDirectMessages: self! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:34'! initialize | buttonWidth inputHeight | super superInitialize. connection _ IRCConnectionEnhanced new. server _ DefaultServer. port _ DefaultPort. nick _ DefaultNick. userName _ DefaultUserName. fullName _ DefaultFullName. self setLabel: 'Enhanced IRC'. self extent: 400 @ 160. buttonWidth _ 120. inputHeight _ 25. self addButtons: (0 @ 0 corner: 0 @ 1) plus: (0 @ 0 corner: buttonWidth @ inputHeight negated). self addConsoleText: (0 @ 0 corner: 1 @ 1) plus: (buttonWidth @ 0 corner: 0 @ inputHeight negated). self addInputPane: (0 @ 1 corner: 1 @ 1) plus: (0 @ inputHeight negated corner: 0 @ 0)! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:03'! makeConnection connection ifNotNil: [connection disconnect]. connection ifNil: [connection _ IRCConnectionEnhanced new]. connection connectToServer: server port: port nick: nick user: userName fullName: fullName. connection requestChannelList! ! !IRCEnhancedMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/18/2002 22:06'! openChannelList "open a view on the list of channels" IRCChannelListBrowserEnhanced openForConnection: connection! ! !PluggableButtonMorph methodsFor: 'accessing' stamp: 'sbw 2/21/2002 21:40'! showSelectionFeedback: boolean showSelectionFeedback _ boolean! ! !PluggableListMorphSpecialHighlighting methodsFor: 'as yet unclassified' stamp: 'sbw 4/13/2003 18:01'! highlightSelection "Do nothing to the text." "scroller submorphs inspect." | skin useSkin colorChoice | false ifFalse: [^ nil]. selectedMorph ifNotNil: [ skin _ nil. skin _ (Smalltalk includesKey: #ImportedSkin) ifTrue: [self owner perform: #skinToUse]. useSkin _ skin isNil not and: [(Preferences perform: #skinListBoxes) and: [colorChoice _ skin perform: #colorListBoxSelectionText. colorChoice isNil not]]. self highlightSpecificMorph: selectedMorph color: (useSkin ifTrue: [colorChoice] ifFalse: [Color red])]! ! !PluggableListMorphSpecialHighlighting methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 09:10'! highlightSpecificMorph: aMorph color: aColor aMorph ifNotNil: [aMorph color: aColor; changed]! ! !PluggableListMorphSpecialHighlighting methodsFor: 'as yet unclassified' stamp: 'sbw 4/13/2003 17:56'! list: listOfStrings "Set the receiver's list as specified" "sbw - special handling. If the list contains StringMorphs use them as-is." | morphList h loc index converter item aSelector | scroller removeAllMorphs. list _ listOfStrings ifNil: [Array new]. list isEmpty ifTrue: [self setScrollDeltas. ^ self selectedMorph: nil]. "NOTE: we will want a quick StringMorph init message, possibly even combined with event install and positioning" font ifNil: [font _ Preferences standardListFont]. converter _ self valueOfProperty: #itemConversionMethod. converter ifNil: [converter _ #asStringOrText]. morphList _ list collect: [:each | item _ each. item isStringMorph ifFalse: [item _ item perform: converter]. item isText ifTrue: [StringMorph contents: item font: font emphasis: (item emphasisAt: 1)] ifFalse: [item isStringMorph ifTrue: [item] ifFalse: [StringMorph contents: item font: font]]]. (aSelector _ self valueOfProperty: #balloonTextSelectorForSubMorphs) ifNotNil: [morphList do: [:m | m balloonTextSelector: aSelector]]. self highlightSelector ifNotNil: [model perform: self highlightSelector with: list with: morphList]. "Lay items out vertically and install them in the scroller" h _ morphList first height. "self listItemHeight" loc _ 0 @ 0. morphList do: [:m | m bounds: (loc extent: 9999 @ h). loc _ loc + (0 @ h)]. scroller addAllMorphs: morphList. index _ self getCurrentSelectionIndex. self selectedMorph: ((index = 0 or: [index > morphList size]) ifFalse: [morphList at: index]). self setScrollDeltas. scrollBar setValue: 0.0. "The following line of code enables us to work both with and without Skins II." (Smalltalk includesKey: #ImportedSkin) ifTrue: [self perform: #setColorsFromSkin]! ! !PluggableMultiColumnListMorph methodsFor: 'initialization' stamp: 'sbw 3/30/2002 13:48'! layoutMorphicLists: arrayOfMorphs | maxWidths offsets locs h | maxWidths _ self calculateColumnWidthsFrom: arrayOfMorphs. offsets _ self calculateColumnOffsetsFrom: maxWidths. locs _ Array new: arrayOfMorphs size. locs at: 1 put: 0 @ 0. 2 to: locs size do: [:indx | locs at: indx put: (offsets at: indx - 1) @ 0]. arrayOfMorphs first isEmpty ifTrue: [^ nil]. h _ arrayOfMorphs first first height. 1 to: arrayOfMorphs size do: [:indx | (arrayOfMorphs at: indx) do: [:morphItem | morphItem bounds: ((locs at: indx) extent: 9999 @ h). locs at: indx put: (locs at: indx) + (0 @ h)]]! ! !PluggableMultiColumnListMorph methodsFor: 'initialization' stamp: 'sbw 4/6/2003 13:51'! list: arrayOfLists | listOfStrings arrayOfMorphs index | lists _ arrayOfLists. scroller removeAllMorphs. listOfStrings _ arrayOfLists == nil ifTrue: [Array new] ifFalse: [arrayOfLists isEmpty ifFalse: [arrayOfLists at: 1]]. list _ listOfStrings ifNil: [Array new]. arrayOfMorphs _ self createMorphicListsFrom: arrayOfLists. self layoutMorphicLists: arrayOfMorphs. list isEmpty ifTrue: [self setScrollDeltas. ^ self selectedMorph: nil]. arrayOfMorphs do: [:morphList | scroller addAllMorphs: morphList]. index _ self getCurrentSelectionIndex. self selectedMorph: ((index = 0 or: [index > (arrayOfMorphs at: 1) size]) ifFalse: [(arrayOfMorphs at: 1) at: index]). self setScrollDeltas. scrollBar setValue: 0.0. self setColorsFromSkin. ! ! !PluggableMultiColumnListMorph methodsFor: 'drawing' stamp: 'sbw 4/6/2003 09:30'! drawOn: aCanvas "Only modification here is to bypass the #drawOn: method in our superclass." super superDrawOn: aCanvas. selectedMorph ifNotNil: [aCanvas fillRectangle: (((scroller transformFrom: self) localBoundsToGlobal: selectedMorph bounds) intersect: scroller bounds) color: self color darker]. self hasFocus ifTrue: [aCanvas frameRectangle: scroller bounds color: Preferences keyboardFocusColor]! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 18:22'! calculateColumnWidthsFrom: arrayOfMorphs | maxWidths | maxWidths _ Array new: arrayOfMorphs size - 1. 1 to: maxWidths size do: [:idx | maxWidths at: idx put: (headerStrings at: idx) width]. 1 to: maxWidths size do: [:idx | (arrayOfMorphs at: idx) do: [:mitem | mitem width > (maxWidths at: idx) ifTrue: [maxWidths at: idx put: mitem width]]]. ^ maxWidths! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 19:21'! createHeaderMorphsFrom: collection | strMorph result headerFont | headerFont _ Preferences windowTitleFont. result _ OrderedCollection new. headerHeight _ 0. collection do: [:str | strMorph _ str isText ifTrue: [StringMorph contents: str font: headerFont emphasis: (str emphasisAt: 1)] ifFalse: [StringMorph contents: str font: headerFont emphasis: 1]. headerHeight < strMorph height ifTrue: [headerHeight _ strMorph height]. result add: strMorph]. headerHeight _ headerHeight + 4. ^ result! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 19:02'! drawHeaderBackgroundOn: aCanvas | colorToUse | colorToUse _ headerBackColor == nil ifTrue: [Color white] ifFalse: [headerBackColor]. aCanvas fillRectangle: self headerRect color: colorToUse! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 12:36'! drawHeaderLinesOn: aCanvas | translate org dst hRect dividerColor | columnOffsets isNil ifTrue: [^nil]. dividerColor _ Color lightGray. hRect _ self headerRect. translate _ hRect topLeft. columnOffsets do: [:each | org _ each x - 1 @ 0 translateBy: translate. dst _ each x - 1 @ headerHeight translateBy: translate. aCanvas line: org to: dst width: 2 color: dividerColor]. org _ hRect topRight. dst _ hRect bottomRight. aCanvas line: org to: dst width: 2 color: dividerColor. org _ hRect bottomLeft. dst _ hRect bottomRight. aCanvas line: org to: dst width: 1 color: dividerColor! ]style[(19 7 4 37 4 35 2 12 3 5 13 5 3 4 14 9 3 5 11 13 8 6 6 3 3 4 5 1 3 1 14 9 5 3 3 4 5 1 3 12 14 9 5 7 11 3 9 3 12 1 12 12 4 3 3 5 12 3 3 5 15 7 9 3 7 3 10 1 10 12 3 3 3 5 14 3 3 5 15 7 9 3 7 3 10 1 10 12)f1b,f1cblue;b,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cred;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c182182106,f1,f1c182182106,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c182182106,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c182182106,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,f1c182182106,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,f1c182182106,f1,f1cblue;i! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 12:37'! drawHeaderStringsOn: aCanvas | hRect translate idx posn | columnOffsets isNil ifTrue: [^nil]. headerStrings == nil ifFalse: [hRect _ self headerRect. translate _ hRect topLeft + (4 @ 2). idx _ 1. headerStrings do: [:each | posn _ columnOffsets at: idx. posn _ posn x @ 0 translateBy: translate. each position: posn. each drawOn: aCanvas. idx _ idx + 1]]! ]style[(21 7 4 25 4 35 2 13 4 3 13 5 3 4 16 9 3 5 12 1 3 1 6 3 3 1 5 13 10 6 8 4 3 13 5 3 7 4 3 4 5 1 14 9 7 4 11 4 7 4 9 7 7 3 3 3 3 1 2)f1b,f1cblue;b,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c182182106,f1,f1c182182106,f1,f1cblue;i,f1,f1c182182106,f1,f1cmagenta;,f1,f1cred;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c182182106,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c182182106,f1! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 19:14'! drawOn: aCanvas super drawOn: aCanvas. self drawHeaderBackgroundOn: aCanvas. self drawHeaderLinesOn: aCanvas. self drawHeaderStringsOn: aCanvas! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 18:48'! headerBackColor: aColor headerBackColor _ aColor! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 19:00'! headerRect ^ (scroller bounds topLeft translateBy: 0 @ headerHeight negated) corner: (scroller bounds topLeft translateBy: (scroller bounds width@0))! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 15:48'! headerStrings: collection "We get a list of strings. Convert them to morphs and then save them." headerStrings _ self createHeaderMorphsFrom: collection! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 17:27'! innerBounds | inner | inner _ super innerBounds copy. headerHeight isNil ifFalse: [ inner _ inner withTop: (inner top + headerHeight) ]. ^ inner! ! !PluggableMultiColumnListMorphByItemWithHeader methodsFor: 'as yet unclassified' stamp: 'sbw 3/5/2002 14:10'! layoutMorphicLists: arrayOfMorphs | maxWidths offsets h | maxWidths _ self calculateColumnWidthsFrom: arrayOfMorphs. offsets _ self calculateColumnOffsetsFrom: maxWidths. columnOffsets _ Array new: arrayOfMorphs size. columnOffsets at: 1 put: 0 @ 0. 2 to: columnOffsets size do: [:indx | columnOffsets at: indx put: (offsets at: indx - 1) @ 0]. arrayOfMorphs first isEmpty ifTrue: [^nil]. h _ arrayOfMorphs first first height. 1 to: arrayOfMorphs size do: [:indx | (arrayOfMorphs at: indx) do: [:morphItem | morphItem bounds: ((columnOffsets at: indx) extent: 9999 @ h). columnOffsets at: indx put: (columnOffsets at: indx) + (0 @ h)]]! ]style[(20 13 4 20 4 9 3 4 28 13 3 7 3 4 29 9 3 13 3 5 6 13 8 13 5 1 6 1 3 1 3 1 7 13 13 6 2 13 5 4 7 7 5 4 3 1 9 1 49 1 3 13 22 1 7 13 13 6 3 13 5 4 11 11 8 9 17 13 5 4 18 4 3 1 8 13 5 4 7 13 5 4 12 1 3 1 3)f1b,f1cblue;b,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c182182106,f1,f1c182182106,f1,f1c182182106,f1,f1c182182106,f1,f1cmagenta;,f1,f1cred;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c182182106,f1,f1c182182106,f1,f1cblue;i,f1,f1cblue;i,f1,f1c182182106,f1,f1cblue;i,f1,f1cred;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cred;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c182182106,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c182182106,f1,f1cblue;i,f1! ! !PluggableMultiColumnListMorphByItemWithHeader class methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 16:08'! on: anObject list: getListSel selected: getSelectionSel changeSelected: setSelectionSel headerStrings: headers | listMorph | listMorph _ self new. listMorph headerStrings: headers. listMorph on: anObject list: getListSel selected: getSelectionSel changeSelected: setSelectionSel menu: nil keystroke: #arrowKey:from:. ^ listMorph! ! !PluggableTextMorph methodsFor: 'menu commands' stamp: 'sbw 1/11/2003 09:26'! replaceSetTextSelector: aSymbol setTextSelector _ aSymbol! ! !PluggableInterceptingTextMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 12:56'! addIntercept: char ^ textMorph addIntercept: char ! ! !PluggableInterceptingTextMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 13:46'! clearInterceptActive ^ textMorph clearInterceptActive! ! !PluggableInterceptingTextMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 13:51'! interceptActive ^ textMorph interceptActive! ! !PluggableInterceptingTextMorph methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 13:29'! setText: aText "The only way we differ from our parent is by specifying a unique edit view class." scrollBar setValue: 0.0. textMorph ifNil: [textMorph _ TextMorphForInterceptingEditView new contents: aText wrappedTo: self innerBounds width - 6. textMorph setEditView: self. scroller addMorph: textMorph] ifNotNil: [textMorph newContents: aText]. self hasUnacceptedEdits: false. self setScrollDeltas! ! !PluggableTextMorphHybrid methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 22:31'! handlesKeyboard: evt ^ true! ! !PluggableTextMorphHybrid methodsFor: 'as yet unclassified' stamp: 'sbw 2/28/2002 22:01'! keyStroke: evt keysInputPane isNil ifFalse: [keysInputPane keyStroke: evt]! ! !PluggableTextMorphHybrid methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 22:42'! keysInputPane: obj keysInputPane _ obj! ! !PluggableTextMorphHybrid methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 23:52'! setText: aText scrollBar setValue: 0.0. textMorph ifNil: [textMorph _ TextMorphForEditViewHybrid new contents: aText wrappedTo: self innerBounds width - 6. textMorph setEditView: self. textMorph keysRecvr: self. scroller addMorph: textMorph] ifNotNil: [textMorph newContents: aText]. self hasUnacceptedEdits: false. self setScrollDeltas! ! !StringMorph methodsFor: 'initialization' stamp: 'sbw 3/1/2002 11:44'! isStringMorph ^true! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/28/2002 00:06'! addPane: swapPane named: aString hidden: hidden closeable: closeable swapModel: swapModel self addSwapPane: swapPane. swapPane name: aString; setProperty: #closeable toValue: closeable; setProperty: #swapModel toValue: swapModel. hidden ifTrue: [self hidePaneNamed: aString] ifFalse: [self showPaneNamed: aString]! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 3/1/2002 23:15'! allSwappedPaneNamesWithModels | panes pane | panes _ OrderedCollection new. tabs do: [:tabName | pane _ self findPaneNamed: tabName. pane isNil ifFalse: [(pane hasProperty: #swapModel) ifTrue: [panes add: (pane valueOfProperty: #swapModel)]]]. ^panes! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 18:34'! bringPaneToFrontNamed: aString self showPaneNamed: aString! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 18:09'! closeAllPanes "Close all the closeable tabs." tabs do: [:paneName | self closePaneNamed: paneName] ! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 18:05'! closeFrontPane self closePane: frontMost! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 18:07'! closePaneNamed: aString self closePane: (self findPaneNamed: aString)! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 18:23'! defaultPaneName: aString defaultPaneName _ aString! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 3/1/2002 23:20'! frontMost ^frontMost! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 3/1/2002 23:17'! frontMostPaneIsCloseable "Answers if the front most pane is closeable. Note if the front most pane is undefined we answer false." frontMost isNil ifFalse: [(frontMost hasProperty: #closeable) ifTrue: [^ frontMost valueOfProperty: #closeable]]. ^ false! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 13:45'! okayToCloseAllPanes! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 18:11'! paneFocusedColor: aColor paneFocusedColor _ aColor! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 18:19'! possibleTabStateChange: paneName self indicateTabChanged: paneName! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 13:44'! removePaneNamed: aString! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 3/10/2002 15:40'! setMoverButtonColors: aColor | bName btn | self moverButtonSpecs do: [:spec | bName _ spec second. btn _ self findPaneNamed: bName. btn isNil ifFalse: [btn color: aColor]]! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 18:34'! showDefaultPane "Show a pane when there is no other pane to show." defaultPaneName isNil ifFalse: [self showPaneNamed: defaultPaneName]! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/28/2002 00:31'! showMover showMover == nil ifTrue: [showMover _ true]. ^showMover! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/28/2002 00:30'! showMover: boolean showMover _ boolean! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 3/1/2002 23:24'! swappedPanesIncludePaneNamed: aString ^tabs includes: aString! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 17:00'! tabFeedbackColor: aColor tabFeedbackColor _ aColor! ! !TabbedSwappingControl methodsFor: 'public API' stamp: 'sbw 2/27/2002 13:45'! updatePaneNamed: aString with: update! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/9/2002 21:46'! addPaneNameToTabs: aString "We're going to add the button too." | swapper btn | swapper _ self findPaneNamed: 'swap'. swapper isNil ifTrue: [^ nil]. btn _ PluggableButtonMorph on: [self showPaneNamed: aString] getState: nil action: #value. btn color: Color white; hResizing: #shrinkWrap; vResizing: #spaceFill; label: aString; onColor: Color white offColor: Color white; feedbackColor: self tabFeedbackColor. swapper addMorphBack: btn. tabs add: aString. ^ btn! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/1/2002 18:54'! addSwapPane: swapPane "private" self addSwapPane: swapPane to: self fractions: (0 @ 0 corner: 1 @ 1) offsets: self swapPaneOffsets! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 2/27/2002 16:41'! addSwapPane: swapPane to: parentMorph fractions: fractions offsets: offsets "private" parentMorph addMorph: swapPane fullFrame: (LayoutFrame fractions: fractions offsets: offsets)! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/1/2002 22:15'! addTabControlMoversToParent: morphParent layout: layout | alignMorph buttons | buttons _ self moverButtons. Preferences alternativeWindowLook ifTrue: [buttons color: Color transparent. buttons submorphsDo: [:m | m borderWidth: 2; borderColor: #raised]]. alignMorph _ AlignmentMorph newRow beSticky; clipSubmorphs: true; layoutInset: 0; cellInset: 0; borderWidth: 0; name: 'movers'; color: Color white. alignMorph addMorph: buttons fullFrame: (LayoutFrame fractions: (0 @ 0 corner: 1 @ 1) offsets: (0 @ 0 corner: 0 @ 0)). morphParent addMorph: alignMorph fullFrame: layout. ! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/9/2002 21:59'! addTabControlPane | alignMorph | alignMorph _ AlignmentMorph newRow. alignMorph beSticky; clipSubmorphs: true; layoutInset: self tabLayoutInset; cellInset: 2; borderWidth: 0; borderColor: Color transparent; name: 'swap'. alignMorph color: Color transparent. self addMorph: alignMorph fullFrame: self tabControlPaneLayout. Preferences alternativeWindowLook ifTrue: [alignMorph borderWidth: 2] ifFalse: [alignMorph borderWidth: 0]. self showMover ifTrue: [self addTabControlMoversToParent: self layout: self tabControlMoversLayout]! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/1/2002 19:01'! findPaneNamed: aString ^ self submorphNamed: aString ifNone: []! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/1/2002 19:25'! initialize: orientation "private" super initialize. self layoutInset: 0@0; cellInset: 2; borderWidth: 0; borderColor: Color transparent; layoutPolicy: ProportionalLayout new. tabs _ Set new. self orientation: orientation. self addTabControlPane! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 2/27/2002 21:12'! moverButtonSpecs "button label, button name, selector, status, help text, on color, off color" ^ #(#('<' 'moveTabLeft' #moveTabLeft #none 'Moves the selected tab to the left' #none #none) #('>' 'moveTabRight' #moveTabRight #none 'Moves the selected tab to the right' #none #none) #('x' 'closeTab' #closeFrontPane #none 'Closes the selected tab' #none #none) )! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/10/2002 15:35'! moverButtons | aRow btn onColor offColor | aRow _ AlignmentMorph newRow beSticky; borderWidth: 0; borderColor: Color transparent. aRow color: Color transparent. aRow clipSubmorphs: true. aRow layoutInset: 1 @ -2; cellInset: 1. self moverButtonSpecs 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; name: spec second; 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]. ^ aRow! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 2/27/2002 16:50'! orientation ^tabQuadrant! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 2/27/2002 16:42'! orientation: aSymbol "private" tabQuadrant _ (#(#top #bottom #left #right ) includes: aSymbol) ifTrue: [aSymbol] ifFalse: [#top]! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 2/27/2002 21:43'! pvtHidePaneNamed: paneName | pane | pane _ self findPaneNamed: paneName. (tabs includes: paneName) ifFalse: [self addPaneNameToTabs: paneName]. pane hide! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 2/27/2002 16:53'! swapPaneOffsets self orientation = #left ifTrue: [^ self swapControlsDimension @ 0 corner: 0 @ 0]. self orientation = #right ifTrue: [^ 0 @ 0 corner: self swapControlsDimension negated @ 0]. self orientation = #bottom ifTrue: [^ 0 @ 0 corner: 0 @ self swapControlsDimension negated]. ^ 0 @ self swapControlsDimension corner: 0 @ 0 ! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/1/2002 22:17'! tabControlMoversLayout self orientation = #left ifTrue: [^ LayoutFrame fractions: (0 @ 0 corner: 0 @ 0) offsets: (0 @ 0 corner: self swapControlsDimension @ self controlMoversDimension)]. self orientation = #right ifTrue: [^ LayoutFrame fractions: (1 @ 1 corner: 1 @ 1) offsets: (self swapControlsDimension negated @ self controlMoversDimension negated corner: 0 @ 0)]. self orientation = #bottom ifTrue: [^ LayoutFrame fractions: (1 @ 1 corner: 1 @ 1) offsets: (self controlMoversDimension negated @ self swapControlsDimension negated corner: 0 @ 0)]. "top" ^ LayoutFrame fractions: (1 @ 0 corner: 1 @ 0) offsets: (self controlMoversDimension negated @ 0 corner: 0 @ self swapControlsDimension)! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/1/2002 19:30'! tabControlPaneLayout self orientation = #left ifTrue: [^ LayoutFrame fractions: (0 @ 0 corner: 0 @ 1) offsets: (0 @ 0 corner: self swapControlsDimension @ 0)]. self orientation = #right ifTrue: [^ LayoutFrame fractions: (1 @ 0 corner: 1 @ 1) offsets: (self swapControlsDimension negated @ 0 corner: 0 @ 0)]. self orientation = #bottom ifTrue: [^ LayoutFrame fractions: (0 @ 1 corner: 1 @ 1) offsets: (0 @ (self swapControlsDimension + 1) negated corner: 0 @ 0)]. "top" ^ LayoutFrame fractions: (0 @ 0 corner: 1 @ 0) offsets: (0 @ 0 corner: self controlMoversDimension negated @ self swapControlsDimension)! ! !TabbedSwappingControl methodsFor: 'private' stamp: 'sbw 3/1/2002 19:27'! tabLayoutInset self orientation = #left ifTrue: [^ 1 @ 2]. self orientation = #right ifTrue: [^ -1 @ 2]. self orientation = #bottom ifTrue: [^ 2 @ -8]. "top" ^ 2 @ 1! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 18:06'! closePane: aPane "If the pane has the closeable property defined AND that property value allows closing, then close the pane." aPane isNil ifTrue: [^ nil]. (aPane hasProperty: #closeable) ifTrue: [(aPane valueOfProperty: #closeable) ifTrue: [self removeSwapPane: aPane]]! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 18:33'! hidePaneNamed: paneName | pane | pane _ self findPaneNamed: paneName. pane isNil ifTrue: [^ nil]. pane = frontMost ifTrue: [^ nil]. (tabs includes: paneName) ifFalse: [self addPaneNameToTabs: paneName]. pane hide! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 20:18'! highlightPaneTabNamed: paneName | swapper btn coll pane btns | pane _ self findPaneNamed: paneName. pane isNil ifTrue: [^ nil]. pane = frontMost ifTrue: [^ nil]. swapper _ self findPaneNamed: 'swap'. swapper isNil ifTrue: [^ nil]. self resetPaneTabColors. btns _ swapper submorphs select: [:m | m isKindOf: PluggableButtonMorph]. coll _ btns select: [:m | m label asString = paneName]. coll isEmpty ifTrue: [^ nil]. btn _ coll first. btn offColor: self paneFocusedColor! ]style[(23 8 4 27 4 4 3 4 16 8 3 4 20 3 4 4 3 9 14 3 4 7 3 4 16 6 3 7 20 3 4 4 22 4 3 7 24 3 2 1 11 20 4 4 3 4 14 3 2 1 18 8 4 4 22 3 4 3 3 4 9 3 11 4 17)f1b,f1cblue;b,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c174174098,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,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,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 20:33'! indicateTabChanged: paneName " is name of pane where something changed while we were on another pane." | swapper coll btn pane btns | pane _ self findPaneNamed: paneName. pane isNil ifTrue: [^ nil]. pane = frontMost ifTrue: [^ nil]. swapper _ self findPaneNamed: 'swap'. swapper isNil ifTrue: [^ nil]. btns _ swapper submorphs select: [:m | m isKindOf: PluggableButtonMorph]. coll _ btns select: [:m | m label asString = paneName]. coll isEmpty ifTrue: [^ nil]. btn _ coll first. btn showSelectionFeedback: true. btn feedbackColor: self tabFeedbackColor! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 20:34'! moveTabLeft | paneName swapper coll btn btns | frontMost isNil ifTrue: [^ nil]. paneName _ frontMost externalName. swapper _ self findPaneNamed: 'swap'. swapper isNil ifTrue: [^ nil]. btns _ swapper submorphs select: [:m | m isKindOf: PluggableButtonMorph]. coll _ btns select: [:m | m label asString = paneName]. coll isEmpty ifTrue: [^ nil]. btn _ coll first. swapper decrementIndexPositionOfSubmorph: btn! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 20:34'! moveTabRight | paneName swapper coll btn btns | frontMost isNil ifTrue: [^ nil]. paneName _ frontMost externalName. swapper _ self findPaneNamed: 'swap'. swapper isNil ifTrue: [^ nil]. btns _ swapper submorphs select: [:m | m isKindOf: PluggableButtonMorph]. coll _ btns select: [:m | m label asString = paneName]. coll isEmpty ifTrue: [^ nil]. btn _ coll first. swapper incrementIndexPositionOfSubmorph: btn! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 21:44'! pvtShowPaneNamed: paneName | pane | pane _ self findPaneNamed: paneName. (tabs includes: paneName) ifFalse: [self addPaneNameToTabs: paneName]. self highlightPaneTabNamed: paneName. pane show! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 18:31'! removeSwapPane: swapPane | paneName pane swapModel okayToClose | paneName _ swapPane externalName. pane _ self findPaneNamed: paneName. pane isNil ifTrue: [^ nil]. (tabs includes: paneName) ifTrue: [(swapPane hasProperty: #swapModel) ifTrue: [swapModel _ swapPane valueOfProperty: #swapModel. okayToClose _ (swapModel respondsTo: #aboutToBeClosed) ifTrue: [swapModel perform: #aboutToBeClosed] ifFalse: [true]. okayToClose ifTrue: [(self removeTabNamed: paneName) ifTrue: [swapPane delete. self showDefaultPane]]]]! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 18:26'! removeTabNamed: aString | swapper btn buttons candidateMorphs | swapper _ self findPaneNamed: 'swap'. swapper isNil ifTrue: [^ false]. buttons _ swapper submorphs select: [:m | m isKindOf: PluggableButtonMorph]. candidateMorphs _ buttons select: [:m | m label = aString]. candidateMorphs isEmpty ifTrue: [^ false]. candidateMorphs size > 1 ifTrue: [^ false]. btn _ candidateMorphs first. tabs remove: aString ifAbsent: [^ false]. btn delete. ^ true! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 3/9/2002 22:01'! resetPaneTabColors | swapper | swapper _ self findPaneNamed: 'swap'. swapper isNil ifTrue: [^ nil]. swapper submorphsDo: [:m | (m isKindOf: PluggableButtonMorph) ifTrue: [m offColor: Color white]]! ! !TabbedSwappingControl methodsFor: 'pane operations' stamp: 'sbw 2/27/2002 18:33'! showPaneNamed: paneName | pane | pane _ self findPaneNamed: paneName. pane isNil ifTrue: [^ nil]. pane = frontMost ifTrue: [^ nil]. frontMost isNil ifFalse: [frontMost hide]. (tabs includes: paneName) ifFalse: [self addPaneNameToTabs: paneName]. self highlightPaneTabNamed: paneName. frontMost _ pane. frontMost show! ! !TabbedSwappingControl methodsFor: 'colors' stamp: 'sbw 2/27/2002 18:12'! paneFocusedColor paneFocusedColor == nil ifTrue: [paneFocusedColor _ Color lightYellow]. ^ paneFocusedColor! ! !TabbedSwappingControl methodsFor: 'colors' stamp: 'sbw 2/27/2002 17:01'! tabFeedbackColor tabFeedbackColor == nil ifTrue: [tabFeedbackColor _ Color lightRed]. ^ tabFeedbackColor! ! !TabbedSwappingControl methodsFor: 'constants' stamp: 'sbw 2/28/2002 00:32'! controlMoversDimension ^self showMover ifTrue: [60] ifFalse: [0]! ! !TabbedSwappingControl methodsFor: 'constants' stamp: 'sbw 2/27/2002 16:49'! swapControlsDimension ^20! ! !TabbedSwappingControl class methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 19:03'! newSwapControlPaneOrientation: orientation showMover: boolean ^ self newColumn showMover: boolean; initialize: orientation! ! !TextMorphForEditViewHybrid methodsFor: 'as yet unclassified' stamp: 'sbw 3/1/2002 00:13'! keyStroke: evt "Pass command keys to the original. UNLESS it's a paste operation. We send that one to the new recvr regardless." | forParent | forParent _ keysRecvr isNil. forParent ifFalse: [ "Need to check if it's a command key..." forParent _ evt commandKeyPressed. forParent ifTrue: [ "Check for special $v PASTE condition." forParent _ evt keyCharacter ~= $v]. forParent ifFalse: [^ keysRecvr keyStroke: evt] ]. super keyStroke: evt ! ! !TextMorphForEditViewHybrid methodsFor: 'as yet unclassified' stamp: 'sbw 2/19/2002 22:41'! keysRecvr: obj keysRecvr _ obj! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 12:49'! addIntercept: char self intercepts add: char! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 16:17'! clearInterceptActive interceptActive _ false! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 12:52'! flushIntercepts self initializeIntercepts! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 3/13/2002 15:35'! giveInterceptedEventToModel: evt "Give event to model. If model returns true then pass evt to super as keystroke." | rcvr | rcvr _ interceptor isNil ifTrue: [self editView model ] ifFalse: [interceptor]. (rcvr keyInterceptedEvent: evt morph: self editView) ifTrue: [super keyStroke: evt]! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 12:48'! initialize super initialize. self initializeIntercepts! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 12:47'! initializeIntercepts intercepts _ Set new! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 16:18'! interceptActive interceptActive == nil ifTrue: [self clearInterceptActive]. ^ interceptActive! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 12:48'! intercepts intercepts == nil ifTrue: [self initializeIntercepts]. ^intercepts! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/24/2002 13:56'! keyStroke: evt "We're not going to let the pane have intercepted keys unless the model says so." self interceptActive ifTrue: [^ self giveInterceptedEventToModel: evt]. (self intercepts includes: evt keyCharacter) ifTrue: [self setInterceptActive. ^self giveInterceptedEventToModel: evt]. super keyStroke: evt! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 12:50'! removeIntercept: char self intercepts remove: char ifAbsent: [nil] ! ! !TextMorphForInterceptingEditView methodsFor: 'as yet unclassified' stamp: 'sbw 2/23/2002 16:18'! setInterceptActive interceptActive _ true! ! !TheWorldMenu methodsFor: 'commands' stamp: 'sbw 2/20/2002 21:12'! openEnhancedIRC "open an interface to Internet Relay Chat" IRCConnectionEnhanced new openWindows! ! !UpdatingThreePhaseButtonMorph methodsFor: '-- all --' stamp: 'sbw 3/3/2002 12:04'! getNewBoolean ^ target perform: getSelector! ! !UpdatingThreePhaseButtonMorph methodsFor: '-- all --' stamp: 'sbw 3/3/2002 12:05'! step | newBoolean | super step. state == #pressed ifTrue: [^ self]. newBoolean _ self getNewBoolean. newBoolean == self isOn ifFalse: [self state: (newBoolean == true ifTrue: [#on] ifFalse: [#off])]! ! !UpdatingThreePhaseButtonMorphEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 11:59'! getArgument: aSymbol getArgument _ aSymbol! ! !UpdatingThreePhaseButtonMorphEnhanced methodsFor: 'as yet unclassified' stamp: 'sbw 3/3/2002 12:06'! getNewBoolean ^ getArgument isNil ifTrue: [target perform: getSelector] ifFalse: [target perform: getSelector with: getArgument] ! ! !IRCProfileDialog reorganize! ('as yet unclassified' addActionButtonsTo:fractions:offsets: addFullNamePaneTo:fractions:offsets: addNickPaneTo:fractions:offsets: addProfileNamePaneTo:fractions:offsets: addServerAddressPaneTo:fractions:offsets: addUserNamePaneTo:fractions:offsets: buildDialogOn: editRowHeight initialExtent labelRowWidth openDialog openDialogOn: openForAddWithOptions: parentDialog: referenceConnection:) ('profile name' buildProfileNamePane profileName profileName:) ('server address' buildServerAddressPane serverAddressString serverAddressString:) ('nick' buildNickPane nick nick:) ('full name' buildFullNamePane fullName fullName:) ('user name' buildUserNamePane userName userName:) ('common entry' buildEntryPane:get:set:entryWidth:canEdit: okToChange) ('local action buttons' buildActionButtons buttonRow buttonSpecs cancelSelected profileSelected windowIsClosing) ('remote window actions' hideWindow noServerSelected serverButton showWindow useValuesFromServer:port:) ! !IRCEnhancedDirectMessagesObserver reorganize! ('as yet unclassified' aboutToBeClosed checkForScrollPlaybackDownOn: checkForScrollPlaybackUpOn: closeFrontMostWindow initialExtent keyInterceptEndedFrom: keyInterceptedEvent:morph: messagePlayback openMorphicView perform:orSendTo:) ('pane building' buildMorphIn: channelTextMenu:) ('text pane' clearTextPane) ('messages' acceptPrivateString: addWithNickToChatText: ircMessageRecieved: talkingTo textWasSent:) ! IRCConnectionEnhanced initialize! !IRCConnectionEnhanced class reorganize! ('release notes - edit strings' featuresString revisionsString toDoString) ('release notes - publish tools' buildReleaseNotesString buildReleaseNotesStringForChangeSetPreamble features installNewPreambleForChangeSetNamed: installNewPreambleForIRCChangeSet releaseNotes revisions toDoList) ('as yet unclassified' initialize postLoadOperation registerInOpenMenu) ('servers list' addServer: buildServersListFrommIRCIniFilePath: buildServersListFrommIRCIniStream: initializeServers noGroupTag removeServer: removeServerNamed:address: serverWithAddress: serversList serversListByGroups) ('options' allOptionKeys checkForMissingOptionsIn: populateOptions: savedOptions) ('message handling support' initializeLocalMessageHandlers initializeMessageHandlers initializeOutgoingMessageHandlers localMessageHandlers outgoingMessageHandlers specialCommands) ('profiles' addDefaultProfile addProfile: checkKeyForProfileNamed: defaultFullName defaultNick defaultUserName fixAllBadProfileKeys getProfileNamed: hasProfileNamed: initializeProfiles profiles removeProfile: removeProfileNamed:) ! !IRCConnectionEnhanced reorganize! ('as yet unclassified' aboutToBeClosed addChannelObserver: channelInfo: channelMembersLoaded: channelObservers channelsBrowser channelsBrowser: channelsBrowserShouldClose clearTextPane connect connectDialog connectDialog: connectDialogShouldClose connectToggle connected consoleMessagePlayback disconnect fullNameButton initialize leave: loadProfile: manualDisconnect msgObserver msgObserver: msgObserverShouldClose newConsole nickButton notifyConfigDialogConnectState openAsMorph openChannelList openConnectionDialogue openDirectMessagesObserver openMotd openWindows port portAsStringButton quit quitMessage reconnectBecauseDropped reconnectChannelChat: reconnectPrivateChat: removeChannelObserver: serverButton subscribedChannelsHavingMemberNamed: talkTo talkTo: updateButtonNamed:newTag: userNameButton windowIsClosing) ('pane building' addActionButtonsToWindow:fractions:offsets: baseWindowTitle buildCombinedConsoleTextAndInputPane buildMorphIn: buttonHeight buttonRow buttonSpecs consoleMenu: initialExtent inputHeight offColor onColor paneColorRamp) ('factored swapper' addSwapPane:named:hidden:closeable:swapModel: alreadyShowingPrivateMessagesFrom: buildSwapper closeTab closeTabs frontMost frontMostPaneIsCloseable possibleTabStateChange:) ('msg process' processIO processPrivmsg:) ('msg process outgoing new' acceptConsoleString: addCrLfAndSendString: convertCmdToProtocolMessage:idString: privmsgFrom:to:text: sendChannelString:from:channelName: sendConsoleString:from: sendLocalMessage:from: sendMessage: sendPrivateString:from:to: sendString:from:to: textWasSent:) ('msg process incoming' extractIdleTimeStringFromProtocolMessage: extractNickIdentifiedStringFromProtocolMessage: extractReplyAwayStringFromProtocolMessage: extractWhoIsChannelsStringFromProtocolMessage: extractWhoIsServerStringFromProtocolMessage: extractWhoIsUserStringFromProtocolMessage: ircMessageRecieved: processChannelList: processChannelListEnd: processChannelsServerLoadTooHigh: processEndOfWhoIs: processIdleTime: processInvite: processJoin: processMessage: processMode: processNamReply: processNickChanged: processNickIdentified: processPart: processQuit: processReplyAway: processReplyWhoIsServer: processReplyWhoIsUser: processWhoIsChannels: sendString:withNickTag:ifFrontChannelObserverIs: serverPrefixMessageNick) ('keys intercept management' checkForScrollPlaybackDownOn: checkForScrollPlaybackUpOn: keyInterceptEndedFrom: keyInterceptedEvent:morph:) ('options management' option: option:value: options populateOptionsFrom: populateOptionsFromDefaults toggleOption:) ! !IRCConnectionDialog reorganize! ('as yet unclassified' addPane:toSwapper:paneName:default: buildSwapper connection: connectionStateChanged hideWindow initialExtent okToChange openDialog showWindow) ('configuration' addConfigurationControlsTo: buildConfigurationDenialPane buildConfigurationPane buttonMorphForConfigurationTag:selector: configurationButtonSpecs denialTrueTypeMorph showConfigDenial: textEntryMorphForConfigurtionSelector:) ('options' buildOptionsPane createOptionButtonForOption:) ('profiles' addProfile addProfile: buildProfileActionButtons buildProfilesList buildProfilesPane deleteProfile disableProfileSelectButton: editProfile editedProfile: horizPaneMargin profileActionButtonColor profileButtonHeight profileButtonSpecs profileDescriptions profilesIndex profilesIndex: saveCurrentToProfile selectProfile vertPaneMargin) ! !IRCChannelObserverEnhanced reorganize! ('as yet unclassified' aboutToBeClosed attemptedNicks calculateWidestNick channelName channelUser channelUsers channelUsersFormatted checkForNickCompletionOn: checkForScrollPlaybackDownOn: checkForScrollPlaybackUpOn: closeFrontMostWindow connection:channelName: findMorphNamed: findMorphNamed:in: firstUnattemptedNickThatMatches: initAttemptedNicks inputPaneHeight inspectChannel ircMessageRecieved: justOpened keyInterceptEndedFrom: keyInterceptedEvent:morph: messagePlayback nick openCapturedView openMorphicView perform:orSendTo: reconnect removeSelfFromConnectionObserversList selectedUser selectedUser: talkToSelectedUser topicStatus topicStatusPrefix updateUsers whoisSelectedUser widestNick windowIsClosing) ('sending' acceptChannelString: textWasSent:) ('panel building' addChannelTextTo:fractions:offsets: addInputPaneTo:fractions:offsets: addTopicPaneTo:fractions:offsets: addUsersListTo:fractions:offsets: buildMorphIn: channelTextMenu: statusMenu: userListMenu:) ('channel pane text' addTextToChannelPane: addTextToChannelPane:usingUnpreparedNick: clearTextPane prepareNickAsRequired:) ('topic pane' canChangeTopic changeTopic: disableTopicEdits enableTopicEdits findTopicMorph topic updateTopicPaneState) ('logging' channelNameWithoutSymbol closeLog ensureIRCLogFileForChannel ensureIRCLogsFolder logChannelTraffic: logFile logFileNameForChannel logsFolder logsFolderName) ! "Postscript: Initialize our stuff." IRCConnectionEnhanced postLoadOperation!