Преглед изворни кода

Squashed 'src/ted2go/' changes from 6c18b98b..0f6470b6

0f6470b6 Merge branch 'dev'
72f317c0 Merge pull request #41 from Hezkore/dev
e5d928e4 Fixed IRC NICK bug
7b3b8448 IRC Updated
4f8b6833 Merge pull request #40 from Hezkore/theme_cleanup
c6545e40 Merge pull request #38 from Hezkore/dev
5d10a3c4 Added missing progressbar for Hollow theme
57e599a5 Theme cleanup
28c86ae0 Changed chat message
4048b74a Chat notification added

git-subtree-dir: src/ted2go
git-subtree-split: 0f6470b6e5e68801ce7fd19b84ded6aa99f8795a
Mark Sibly пре 8 година
родитељ
комит
e8570be4a4

+ 62 - 4
MainWindow.monkey2

@@ -11,6 +11,7 @@ Namespace ted2go
 
 #Import "assets/fonts/@/fonts"
 
+#Import "assets/themes/irc/@/themes/irc"
 
 Global MainWindow:MainWindowInstance
 
@@ -57,13 +58,17 @@ Class MainWindowInstance Extends Window
 		End
 		
 		'IRC tab
+		
 		_ircView=New IRCView
-		_ircView.introScreen.Text="Get live help from other Monkey 2 users"
+		_ircView.introScreen.Text="Hang out with other Monkey 2 users"
 		_ircView.introScreen.OnNickChange+=Lambda( nick:String )
 			Prefs.IrcNickname=nick
 		End
+		
 		SetupChatTab()
 		
+		If Prefs.IrcConnect Then _ircView.introScreen.Connect()
+		
 		'Build tab
 		
 		_buildConsole=New ConsoleExt
@@ -367,6 +372,8 @@ Class MainWindowInstance Extends Window
 		_consolesTabView.AddTab( "Find",_findConsole,False )
 		_consolesTabView.AddTab( "Chat",_ircView,False )
 		
+		_consolesTabView.CurrentChanged+=OnChatClicked
+		
 		_statusBar=New StatusBarView
 		
 		_contentView=New DockingView
@@ -506,13 +513,14 @@ Class MainWindowInstance Extends Window
 	End
 	
 	Method Terminate()
-	
+		
 		_isTerminating=True
 		SaveState()
 		_enableSaving=False
 		OnForceStop() ' kill build process if started
 		ProcessReader.StopAll()
-
+		If _ircView Then _ircView.Quit("Closing Ted2Go")
+		
 		App.Terminate()
 	End
 
@@ -956,7 +964,7 @@ Class MainWindowInstance Extends Window
 	Field _helpIdent:String
 	
 	Method OnRender( canvas:Canvas ) Override
-	
+		
 		If Not _inited
 			_inited=True
 			OnInit()
@@ -969,6 +977,8 @@ Class MainWindowInstance Extends Window
 			SizeChanged()
 		Endif
 		
+		UpdateIrcIcon()
+		
 		Rendered()
 		Rendered=Null
 	End
@@ -995,6 +1005,8 @@ Class MainWindowInstance Extends Window
 		
 		If Not _ircView Return
 		
+		_ircView.ircHandler.OnMessage+=Self.OnChatMessage
+		
 		Local intro:=_ircView.introScreen
 		
 		If intro.IsConnected Return
@@ -1007,6 +1019,49 @@ Class MainWindowInstance Extends Window
 		
 	End
 	
+	Method OnChatClicked()
+		If _consolesTabView.CurrentView<>_ircView Then Return
+		
+		_consolesTabView.SetTabIcon( _ircView, Null )
+		_ircNotifyIcon=0
+	End
+	
+	Method OnChatMessage( message:IRCMessage, container:IRCMessageContainer, server:IRCServer )
+		If message.type<>"PRIVMSG" Or _consolesTabView.CurrentView=_ircView Then Return
+		
+		'Show notice icon
+		If message.text.Contains(server.nickname) Then
+			If _ircNotifyIcon<=1 Then _ircNotifyIcon=2
+	
+		Else
+			If _ircNotifyIcon<=0 Then _ircNotifyIcon=1
+		Endif
+		
+	End
+	
+	Method UpdateIrcIcon()
+		If _ircNotifyIcon<=0 Then Return
+		
+		Local time:Int=Int(Millisecs()*0.0025)
+		
+		If time=_ircIconBlink Then Return
+		_ircIconBlink=time
+		
+		If time Mod 2 Then
+			Select _ircNotifyIcon
+				
+				Case 1
+					_consolesTabView.SetTabIcon( _ircView, App.Theme.OpenImage( "irc/notice.png" ) )
+					
+				Case 2
+					_consolesTabView.SetTabIcon( _ircView, App.Theme.OpenImage( "irc/important.png" ) )
+			End
+		Else
+			_consolesTabView.SetTabIcon( _ircView, App.Theme.OpenImage( "irc/blink.png" ) )
+		Endif
+		
+	End
+	
 	Method LoadState( jobj:JsonObject )
 	
 		If jobj.Contains( "browserSize" )
@@ -1137,6 +1192,9 @@ Class MainWindowInstance Extends Window
 	Field _consolesTabView:TabView
 	Field _browsersTabView:TabView
 	
+	Field _ircNotifyIcon:Int
+	Field _ircIconBlink:Int
+	
 	Field _forceStop:Action
 
 	Field _tabMenu:Menu

+ 5 - 1
Prefs.monkey2

@@ -21,7 +21,8 @@ Class Prefs
 	Global IrcNickname:String
 	Global IrcServer:="irc.freenode.net"
 	Global IrcPort:=6667
-	Global IrcRooms:="#monkey2" '#monkey2Ui#monkey23D
+	Global IrcRooms:="#monkey2" '#mojox#mojo2d
+	Global IrcConnect:Bool=False
 	'
 	Global EditorToolBarVisible:=False
 	Global EditorGutterVisible:=True
@@ -46,6 +47,8 @@ Class Prefs
 			IrcServer=Json_GetString( j2,"server",IrcServer )
 			IrcPort=Json_GetInt( j2,"port",IrcPort )
 			IrcRooms=Json_GetString( j2,"rooms",IrcRooms )
+			IrcConnect=Json_GetBool( j2,"connect",IrcConnect )
+			
 		Endif
 		
 		If json.Contains( "main" )
@@ -108,6 +111,7 @@ Class Prefs
 		j["server"]=New JsonString( IrcServer )
 		j["port"]=New JsonNumber( IrcPort )
 		j["rooms"]=New JsonString( IrcRooms )
+		j["connect"]=New JsonBool( IrcConnect )
 		
 		j=New JsonObject
 		json["completion"]=j

BIN
assets/themes/hollow_assets/checkbox_icons.png


BIN
assets/themes/hollow_assets/dialog_skin.png


BIN
assets/themes/hollow_assets/progressbar_icons.png


BIN
assets/themes/hollow_assets/tabclose_icons.png


BIN
assets/themes/hollow_assets/treeview_icons.png


BIN
assets/themes/irc/blink.png


BIN
assets/themes/irc/important.png


BIN
assets/themes/irc/notice.png


BIN
assets/themes/prime_assets/button_skin.png


BIN
assets/themes/prime_assets/square.png


BIN
assets/themes/prime_assets/tabbutton_skin.png


+ 474 - 0
assets/themes/theme-hollow.json

@@ -0,0 +1,474 @@
+//Theme by @Hezkore
+//Inspired by gruvbox
+{
+	"extends":"ted2-default",
+	
+	"fonts":{
+	
+		"normal":"Roboto-Medium.ttf,16",
+		"fixedWidth":"RobotoMono-Medium.ttf,19",
+		"small":"Roboto-Medium.ttf,14",
+		"editor":"MesloLGSDZ-Bold.ttf,19"
+	},
+
+	"colors":{
+
+		"border": "#121111",
+
+		"transparent": "#0000",
+		"darkest": "#222020",
+		"darker": "#32302F",
+		"dark": "#3C3836",
+		"bright": "#504945",
+		"brighter": "#665C54",
+		"brightest": "#7C6F64",
+
+		"text-default": "#CECECE",
+		"text-highlight": "#FFFFFF",
+		"text-disabled": "#6F6F6F",
+		"text-background": "#969696",
+		
+		"textview-cursor":"textview-color1",
+		"textview-selection":"brightest",
+		"textview-cursor-line":"dark",
+		"textview-whitespaces":"bright",
+
+		"textview-color0":"#FF00FF", 	//When is this used?
+		"textview-color1": "#EBDAB4",	//identifiers
+		"textview-color2": "#FB4934",	//keywords
+		"textview-color3": "#B8BB26",	//strings
+		"textview-color4": "#B087D2",	//numbers
+		"textview-color5": "#6F665E",	//comment
+		"textview-color6": "#FABD2F",	//preproc
+		"textview-color7": "#8EC07C",
+		
+		"windowClearColor":"darkest",
+		"menu-shortcut":"text-background",
+
+		"statusbar": "darkest",
+		"statusbar-active": "darkest"
+	},
+	
+	"styles":{
+
+		"default":{
+			"font":"normal",
+			"textColor":"text-default",
+			"iconColor":"#ffff",
+			"states":{
+				"disabled":{
+					"textColor":"text-disabled",
+					"iconColor":"#8fff"
+				}
+			}
+		},
+
+		"CodeMapView":{
+			"extends":"TextView",
+			"margin":[ 4,0,0,0 ]
+		},
+
+		"GutterView":{
+			"padding":[ -16,0,16,0 ],
+			"extends":"TextView",
+			"textColor":"text-disabled",
+			"backgroundColor":"darkest",
+			"font":"editor"
+		},
+		
+		"DebugToolBar":{
+			"extends":"ToolBar",
+			"border":[ 1 ],
+			"borderColor":"darker",
+			"icons":"debug_icons.png"
+		},
+		
+		"HelpTextField":{
+			"extends":"TextField",
+			"skinColor":"darker"
+		},
+
+		"Hint":{
+			"font":"small",
+			"textColor":"text-default",
+			"backgroundColor":"border"
+		},
+
+		"ToolBarExt":{
+			"extends":"ToolBar",
+			"padding":[ 0 ],
+			"margin":[ 0 ],
+			"backgroundColor":"dark",
+			"border":[ 0 ]
+		},
+
+		"MainToolBar":{
+			"extends":"ToolBarExt"
+		},
+
+		"EditorToolBar":{
+			"extends":"ToolBarExt"
+		},
+
+		"SourceToolBar":{
+			"extends":"ToolBarExt",
+			"backgroundColor":"darker"
+		},
+
+		"TabViewArrowPrev":{
+			"extends":"Button",
+			"textColor":"text-background",
+			"padding":[ 8,4 ],
+			"margin":[ 2,0,2,0 ]
+		},
+
+		"TabViewArrowNext":{
+			"extends":"Button",
+			"textColor":"text-background",
+			"padding":[ 8,4 ],
+			"margin":[ 2,0,12,0 ]
+		},
+
+		"ProgressBar":{
+			"icons":"hollow_assets/progressbar_icons.png"
+		},
+
+		"StatusBar":{
+			"extends":"DockingView",
+			"padding":[ 0,8,0,8 ],
+			"border":[0,1,0,0],
+			"borderColor":"border",
+			"backgroundColor":"statusbar"
+		},
+		"StatusBarText":{
+			"extends":"Label",
+			"font":"small"
+		},
+		"StatusBarLineInfo":{
+			"extends":"Label",
+			"margin":[ 40,0,0,0 ],
+			"font":"small"
+		},
+		"StatusBarIns":{
+			"extends":"Label",
+			"font":"small"
+		},
+		"StatusBarProgress":{
+			"extends":"ProgressBar",
+			"margin":[ 6,0 ]
+		},
+
+		"CompletionDialog":{
+			"extends":"Dialog"
+		},
+		"CompletionDialogContent":{
+			"padding":[ 1 ]
+		},
+		
+		"ProjectTabView":{
+			"extends":"TabView",
+			"backgroundColor":"dark"
+		},
+		
+		"StatusBarButton":{
+			"extends":"ToolButton",
+			"padding":[ 0 ],
+			"skinColor":"transparent"
+		},
+		
+		"Label":{
+			"padding":[8,4]
+		},
+		
+		"Button":{
+			"extends":"Label",
+			"padding":[4,2],
+			"backgroundColor":"dark",
+			"textColor":"text-default",
+			
+			"states":{
+				"hover":{
+					"textColor":"text-highlight",
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"textColor":"text-highlight",
+					"backgroundColor":"brighter"
+				},
+				"selected":{
+					"textColor":"text-highlight",
+					"backgroundColor":"brightest"
+				}
+			}
+		},
+
+		"ToolButton":{
+			"extends":"Button",
+			"backgroundColor":"dark",
+			"padding":[ 2,0 ],
+			"margin":[ 2,0 ],
+
+			"states":{
+				"hover":{
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"backgroundColor":"brighter"
+				},
+				"selected":{
+					"backgroundColor":"brighter"
+				}
+			}
+		},
+		
+		"PushButton":{
+			"extends":"Button",
+			"margin":[4,4]
+		},
+		
+		"CheckButton":{
+			"extends":"Label"
+		},
+		
+		"CheckBox":{
+			"icons":"hollow_assets/checkbox_icons.png",
+			"iconColor":"textview-color7",
+			"margin":[0,0,0,0]
+		},
+		
+		"ScrollView":{
+		},
+		
+		"ScrollBar":{
+			"backgroundColor":"darker"
+		},
+		
+		"ScrollKnob":{
+			"padding":[ 6 ],
+			"border":[ 0 ],
+			"backgroundColor":"dark",
+			"states":{
+				"hover":{
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"backgroundColor":"brighter"
+				}
+			}
+		},
+		
+		"TextView":{
+			"font":"editor",
+			"textColor":"textview-color7",
+			"backgroundColor":"darker"
+		},
+		
+		"TextViewContent":{
+			"padding":[4]
+		},
+		
+		"TextField":{
+			"font":"normal",
+			"padding":[ 2 ],
+			"margin":[ 2 ],
+			"backgroundColor":"darker"
+		},
+		
+		"DockingView":{
+		},
+		
+		"DockedView":{
+		},
+		
+		"DockKnob":{
+			"padding":[ 3 ],
+			"backgroundColor":"darkest",
+			"states":{
+				"hover":{
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"backgroundColor":"brightest"
+				}
+			}
+		},
+		
+		"ToolBar":{
+			"padding":[ 2 ],
+			"backgroundColor":"darker"
+		},
+		
+		"Menu":{
+			"extends":"DockingView",
+			"padding":[ 0 ],
+			"skin":"hollow_assets/dialog_skin.png",
+			"skinColor":"dark"
+		},
+		
+		"MenuButton":{
+			"extends":"Label",
+			"padding":[8,3],
+			"textColor":"text-default",
+			"states":{
+				"hover":{
+					"textColor":"text-highlight",
+					"backgroundColor":"brightest"
+				},
+				"active":{
+					"backgroundColor":"brightest"
+				},
+				"selected":{
+					"backgroundColor":"brightest"
+				}
+			}
+		},
+		
+		"MenuBar":{
+			"extends":"ToolBar",
+			"backgroundColor":"border",
+			"border":[ 0,0,0,1 ],
+			"backgroundColor":"dark",
+			"margin":[ 0 ]
+		},
+		
+		"MenuSeparator":{
+			"padding":[ 0,0,0,1 ],
+			"backgroundColor":"dark",
+			"border":[ 8,8,7,7 ]
+		},
+
+		"TabView":{
+		},
+		
+		"TabBar":{
+			"extends":"ToolBar",
+			"padding":[ 0,0,0,0 ],
+			"backgroundColor":"windowClearColor"
+		},
+		
+		"TabButton":{
+			"extends":"Button",
+			"font":"small",
+			"padding":[10],
+			"textColor":"text-disabled",
+			"backgroundColor":"darkest",
+			"borderColor":"darkest",
+			"border":[0,2,0,0],
+			"states":{
+				"hover":{
+					"textColor":"text-background"
+				},
+				"active":{
+					"textColor":"text-background"
+				},
+				"selected":{
+					"borderColor":"textview-color7",
+					"backgroundColor":"darker",
+					"textColor":"text-default"
+				}
+			}
+		},
+		
+		"TabClose":{
+			"margin":[32,0,0,0 ],
+			"icons":"hollow_assets/tabclose_icons.png",
+			"iconColor":"text-disabled",
+			"states":{
+				"hover":{
+					"iconColor":"text-background"
+				},
+				"active":{
+					"iconColor":"text-highlight"
+				}
+			}
+		},
+		
+		"TableView":{
+			"extends":"DockingView"
+		},
+		
+		"TableHeader":{
+			"extends":"Label",
+			"textColor":"text-highlight",
+			"borderColor":"darker"
+		},
+		
+		"TableColumn":{
+		},
+		
+		"TreeView":{
+			"backgroundColor":"darker",
+			"icons":"hollow_assets/treeview_icons.png",
+			"iconColor":"text-background"
+		},
+		
+		"TreeViewContent":{
+			"padding":[3]
+		},
+
+		"TreeViewNode":{
+			"font":"small",
+			"textColor":"text-background",
+			"padding":[0,2,0,2],
+			"states":{
+				"hover":{
+					"textColor":"text-default"
+				},
+				"selected":{
+					"textColor":"text-default"
+				}
+			}
+		},
+		
+		"ListView":{
+			"backgroundColor":"dark"
+		},
+		
+		"ListViewContent":{
+			"padding":[2]
+		},
+		
+		"ListViewItem":{
+			"padding":[1],
+			"states":{
+				"hover":{
+					"backgroundColor":"darker"
+				},
+				"selected":{
+					"backgroundColor":"darker",
+					"textColor":"text-highlight"
+				}
+			}
+		},
+		
+		"FileBrowser":{
+			"extends":"TreeView"
+		},
+		
+		"HtmlView":{
+		},
+		
+		"Console":{
+			"backgroundColor":"darker"
+		},
+		
+		"Dialog":{
+			"skin":"hollow_assets/dialog_skin.png",
+			"skinColor":"bright"
+		},
+		
+		"DialogTitle":{
+			"extends":"Label",
+			"backgroundColor":"brightest"
+		
+		},
+		
+		"DialogContent":{
+			"padding":[ 8,8,8,4 ]
+		},
+		
+		"DialogActions":{
+			"padding":[ 8,4,8,4 ]
+		}
+	}
+}

+ 28 - 19
assets/themes/theme-prime-base.json

@@ -40,7 +40,7 @@
 		"textview-color4": "#6C71C4",	//numbers
 		"textview-color5": "#606060",	//comment
 		"textview-color6": "#E74C31",	//preproc
-		"textview-color7": "#FF00FF",	//When is this used?
+		"textview-color7": "#3298DB",
 		
 		"windowClearColor":"bright",
 		"menu-shortcut":"text-background",
@@ -111,8 +111,21 @@
 
 		"ToolButton":{
 			"extends":"Button",
+			"backgroundColor":"dark",
 			"padding":[ 2,0 ],
-			"margin":[ 2,0 ]
+			"margin":[ 2,0 ],
+
+			"states":{
+				"hover":{
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"backgroundColor":"brighter"
+				},
+				"selected":{
+					"backgroundColor":"brighter"
+				}
+			}
 		},
 
 		"TabViewArrowPrev":{
@@ -180,21 +193,20 @@
 		"Button":{
 			"extends":"Label",
 			"padding":[4,2],
-			"skin":"prime_assets/square.png",
-			"skinColor":"darker",
+			"backgroundColor":"darker",
 			
 			"states":{
 				"hover":{
 					"textColor":"accent",
-					"skinColor":"dark"
+					"backgroundColor":"dark"
 				},
 				"active":{
 					"textColor":"accent",
-					"skinColor":"dark"
+					"backgroundColor":"dark"
 				},
 				"selected":{
 					"textColor":"accent",
-					"skinColor":"dark"
+					"backgroundColor":"dark"
 				}
 			}
 		},
@@ -222,22 +234,22 @@
 		},
 		
 		"ScrollKnob":{
-			"padding":[ 4 ],
+			"padding":[ 6 ],
 			"border":[ 0 ],
-			"skin":"prime_assets/square.png",
-			"skinColor":"brightest",
+			"backgroundColor":"brightest",
 			"states":{
 				"hover":{
-					"skinColor":"brighter"
+					"backgroundColor":"brighter"
 				},
 				"active":{
-					"skinColor":"brightest"
+					"backgroundColor":"brightest"
 				}
 			}
 		},
 		
 		"TextView":{
 			"font":"editor",
+			"textColor":"textview-color7",
 			"backgroundColor":"dark"
 		},
 		
@@ -247,10 +259,9 @@
 		
 		"TextField":{
 			"font":"normal",
-			"padding":[ 2 ],
+			"padding":[ 4 ],
 			"margin":[ 2 ],
-			"skin":"prime_assets/square.png",
-			"skinColor":"darker"
+			"backgroundColor":"darker"
 		},
 		
 		"DockingView":{
@@ -325,24 +336,22 @@
 		},
 		
 		"TabButton":{
-            "skinColor":"brighter",
+            "backgroundColor":"brighter",
 			"extends":"Button",
 			"font":"small",
 			"padding":[14,12,14,12],
 			"border":[0,0,0,4],
 			"borderColor":"accentDark",
-			"skin":"prime_assets/square.png",
 			"textColor":"text-background",
 			"states":{
 				"hover":{
-					"skinColor":"bright",
 					"textColor":"text-default"
 				},
 				"active":{
 					"textColor":"text-default"
 				},
 				"selected":{
-					"skinColor":"brightest",
+					"backgroundColor":"brightest",
 					"textColor":"text-highlight",
 					"borderColor":"accent"
 				}

+ 2 - 1
assets/themes/theme-smooth.json

@@ -61,7 +61,8 @@
 		},
 
 		"GutterView":{
-			"border":[0],
+			"border":[0,0,1,0],
+			"borderColor":"textview-whitespaces",
 			"extends":"TextView",
 			"textColor":"text-disabled"
 		},

+ 1 - 0
assets/themes/themes.json

@@ -4,6 +4,7 @@
 	"Monkey 1":"theme-monkey1",
 	"Blitzed":"theme-blitzed",
 	"Basic Blue":"theme-Basic-Blue",
+	"Hollow":"theme-hollow",
 	"Prime - Red":"theme-prime-red",
 	"Prime - Blue":"theme-prime-blue",
 	"Smooth":"theme-smooth",

+ 6 - 1
dialog/PrefsDialog.monkey2

@@ -75,6 +75,7 @@ Class PrefsDialog Extends DialogExt
 	Field _chatServer:TextField
 	Field _chatPort:TextField
 	Field _chatRooms:TextField
+	Field _chatAutoConnect:CheckButton
 	
 	Method OnApply()
 	
@@ -109,6 +110,7 @@ Class PrefsDialog Extends DialogExt
 		Prefs.IrcServer=_chatServer.Text
 		Prefs.IrcPort=Int(_chatPort.Text)
 		Prefs.IrcRooms=_chatRooms.Text
+		Prefs.IrcConnect=_chatAutoConnect.Checked
 		
 		App.ThemeChanged()
 		
@@ -293,11 +295,13 @@ Class PrefsDialog Extends DialogExt
 	
 	Method GetChatDock:DockingView()
 		
-		Local chatTable:=New TableView( 2,4 )
+		Local chatTable:=New TableView( 2,5 )
 		_chatNick=New TextField( Prefs.IrcNickname )
 		_chatServer=New TextField( Prefs.IrcServer )
 		_chatPort=New TextField( ""+Prefs.IrcPort )
 		_chatRooms=New TextField( Prefs.IrcRooms )
+		_chatAutoConnect=New CheckButton( "Auto connect at start" )
+		_chatAutoConnect.Checked=Prefs.IrcConnect
 		chatTable[0,0]=New Label( "Nickname" )
 		chatTable[1,0]=_chatNick
 		chatTable[0,1]=New Label( "Server" )
@@ -306,6 +310,7 @@ Class PrefsDialog Extends DialogExt
 		chatTable[1,2]=_chatPort
 		chatTable[0,3]=New Label( "Rooms" )
 		chatTable[1,3]=_chatRooms
+		chatTable[0,4]=_chatAutoConnect
 		
 		Local docker:=New DockingView
 		docker.AddView( New Label( " " ),"top" )

+ 225 - 30
view/IRCView.monkey2

@@ -3,8 +3,6 @@
 
 Namespace ted2go
 
-#Import "assets/"
-
 '=MODULE=
 
 'This class is the main class
@@ -56,12 +54,12 @@ Class IRCServer Extends IRCMessageContainer
 	Field serverPort:Int
 	Field sendBuffer:DataBuffer
 	Field receiveBuffer:DataBuffer=New DataBuffer(512)
-	Field fiberSleep:Float=0.26 'Lower value gets internet messages faster but uses more CPU
+	Field fiberSleep:Float=0.35 'Lower value gets internet messages faster but uses more CPU
 	Field updateFiber:Fiber 'Fiber for updating internet mesages
 	Field socket:Socket
 	Field stream:SocketStream
 	Field nickname:String
-	Field realname:String="KoreIRC"
+	Field realname:String="KoreIRC 1.0"
 	Field messageContainers:=New List<IRCMessageContainer>
 	Field skipContainers:=New String[]("chanserv","nickserv","services","*") 'Make sure these are lower case!
 	
@@ -386,10 +384,10 @@ Class IRCServer Extends IRCMessageContainer
 					Endif
 					
 				Case "NICK" 'Changing names
-					'Update local name
+					'Was local name?
 					Local wasSelf:Bool
 					If GetNickname(fromUser)=nickname Then
-						wasSelf=true
+						wasSelf=True
 						nickname=msg
 					Endif
 					
@@ -399,8 +397,12 @@ Class IRCServer Extends IRCMessageContainer
 							If u.name=GetNickname(fromUser) Then
 								u.name=msg
 								container.SortUsers()
-								TriggerOnMessage(msg,fromUser,msg,type,container)
-								parent.OnUserUpdate(container,Self)
+								
+								If Not wasSelf Then 
+									TriggerOnMessage(msg,fromUser,msg,type,container)
+									parent.OnUserUpdate(container,Self)
+								Endif
+								
 								Exit
 							Endif
 						Next
@@ -464,11 +466,19 @@ Class IRCServer Extends IRCMessageContainer
 		nC.type=type
 		messageContainers.AddLast(nC)
 		parent.OnNewContainer(nC,Self)
+		
+		'Load history for chat rooms
+		If nC.name.StartsWith("#") Then nC.LoadHistory()
+		
 		Return nC
 	End
 	
 	'Remove a specific message container
 	Method RemoveMessageContainer(container:IRCMessageContainer)
+		
+		'Save history for chat rooms
+		If container.name.StartsWith("#") Then container.SaveHistory()
+		
 		messageContainers.Remove(container)
 		If parent Then parent.OnRemoveContainer(container,Self)
 	End
@@ -507,6 +517,50 @@ Class IRCMessageContainer
 	Field gotUsers:Bool 'Have we gotten users before?
 	Field messages:=New List<IRCMessage>
 	
+	Method LogPath:String()
+		
+		Return AppDir() + "/logs/" + parent.serverAddress + "/" + name + ".txt"
+		
+	End
+	
+	Method LoadHistory()
+		Local file:String=LoadString( Self.LogPath() )
+		If Not file Then Return
+		
+		Local lines:=file.Split( "~n" )
+		Local type:String
+		Local time:String
+		Local user:String
+		Local message:String
+		
+		For Local s:=Eachin lines
+			If Not s.Contains( ">" ) Or Not s.Contains( " " ) Or Not s.Contains( ":" ) Then Continue
+			
+			type=s.Split( ">" )[0]
+			time=s.Split( ">" )[1].Split( " " )[0]
+			user=s.Split( ">" )[1].Split( " " )[1].Split( ":" )[0]
+			message=s.Split( type + ">" + time + " " + user + ":" )[1]
+			
+			Self.AddMessage( message, user, Self.name, type.ToUpper() ).time=time
+		Next
+		
+	End
+	
+	Method SaveHistory()
+		Local log:String
+		
+		For Local m:=Eachin Self.messages
+			If m.type<>"PRIVMSG" And m.type<>"QUIT" And m.type<>"PART" And m.type<>"JOIN" And m.type<>"NICK" Then Continue
+			
+			log+=m.type+">"+m.time+" "+m.fromUser+":"+m.text+"~n"
+		Next
+		
+		If log.Length>2 Then
+			CreateFile( LogPath(), True )
+			SaveString( log, LogPath() )
+		Endif
+	End
+	
 	Method Remove() Virtual
 		parent.messageContainers.Remove(Self)
 	End
@@ -520,7 +574,7 @@ Class IRCMessageContainer
 	End
 	
 	'Add a message to this container
-	Method AddMessage(msg:String,fromUser:String="",toUser:String="",type:String="",hostname:string="")
+	Method AddMessage:IRCMessage(msg:String,fromUser:String="",toUser:String="",type:String="",hostname:string="")
 		Local nM:=New IRCMessage
 		nM.parent=Self
 		nM.text=msg
@@ -529,6 +583,7 @@ Class IRCMessageContainer
 		nM.type=type
 		nM.hostname=hostname
 		messages.AddLast(nM)
+		Return nM
 	End
 	
 	'Sort the userlist
@@ -580,6 +635,75 @@ End
 
 '=IRC VIEW=
 
+'Highlighter for IRC history text
+Function IrcTextHighlighter:Int( text:String,colors:Byte[],sol:Int,eol:Int,state:Int )
+	Local i0:=sol
+	Local msgStep:Int
+	Local userColor:Int
+	Local userStart:Int
+	Local userEnd:Int
+	Local userDone:Bool
+	
+	While i0<eol
+		Local chr:=text[i0]
+		
+		If userDone Then 
+			colors[i0]=0
+		Else
+			colors[i0]=1
+		Endif
+		
+		'Reset
+		If chr=110 And msgStep=6 Then 'n
+			msgStep=0
+			userDone=False
+		Elseif msgStep=6 And chr<>110
+			msgStep=5
+		Endif
+		If chr=126 And msgStep=5 Then '~
+			msgStep=6
+		Endif
+		
+		'Detect username
+		If chr=9 And msgStep=2 Then 'Tab
+			userStart=i0
+			msgStep=3
+			userColor=0
+		Elseif msgStep=2 And chr<>9 Then
+			msgStep=5
+		Endif
+		If chr=32 And msgStep=4 Then 'Space after :
+			userEnd=i0-1
+			msgStep=5
+			userDone=True
+			For Local i1:Int=userStart Until userEnd
+				colors[i1]=2 + (userColor Mod 5)
+			Next
+		Elseif msgStep=4 And chr<>32 Then
+			msgStep=5
+		Endif
+		If chr=58 And msgStep=3 Then ':
+			msgStep=4
+		Endif
+		
+		If msgStep=3 Then userColor+=chr
+		
+		'Detect time
+		If chr=91 And msgStep=0 Then '[
+			msgStep=1
+		Endif
+		If msgStep=1 Then colors[i0]=1
+		
+		If chr=93 And msgStep=1 Then ']
+			msgStep=2
+		Endif
+		
+		i0+=1
+	Wend
+	
+	Return state
+End
+
 'This is a pre-made IRC client, ready to be used in any MojoX application
 Class IRCView Extends DockingView
 	Field ircHandler:IRC
@@ -598,6 +722,8 @@ Class IRCView Extends DockingView
 	Field selectedServer:IRCServer
 	Field selectedMessageContainer:IRCMessageContainer
 	
+	Field maxHistory:Int=50
+	
 	Property Intro:IRCIntroView()
 		Return introScreen
 	End
@@ -615,6 +741,7 @@ Class IRCView Extends DockingView
 		
 		'Chat history field
 		historyField=New TextView
+		historyField.Document.TextHighlighter=IrcTextHighlighter
 		historyField.ReadOnly=True
 		historyField.WordWrap=True
 		chatScreen.ContentView=historyField
@@ -859,7 +986,7 @@ Class IRCView Extends DockingView
 			selectedMessageContainer.AddMessage(selectedServer.nickname+": "+text)
 		Else
 			'Yep, add PRIVMSG and send!
-			selectedMessageContainer.AddMessage(selectedServer.nickname+": "+text)
+			selectedMessageContainer.AddMessage( text, selectedServer.nickname, selectedMessageContainer.name, "PRIVMSG" )
 			text="PRIVMSG "+selectedMessageContainer.name+" :"+text
 		Endif
 		
@@ -867,6 +994,23 @@ Class IRCView Extends DockingView
 		AddChatMessage(selectedMessageContainer.messages.Last)
 	End
 	
+	Method SaveAllHistory()
+		For Local s:IRCServer=Eachin Self.ircHandler.servers
+		For Local c:IRCMessageContainer=Eachin s.messageContainers
+			c.SaveHistory()
+		Next
+		Next
+	End
+	
+	Method Quit(message:String=Null)
+		SaveAllHistory()
+		
+		For Local s:IRCServer=Eachin Self.ircHandler.servers
+			s.SendString("QUIT :"+message)
+			s.Disconnect()
+		Next
+	End
+	
 	Method OnMessageIRC(message:IRCMessage,container:IRCMessageContainer,server:IRCServer)
 		'For message information, visit: https://tools.ietf.org/html/rfc2812
 		Local doNotify:Bool 'Should we notify the user?
@@ -877,48 +1021,45 @@ Class IRCView Extends DockingView
 				
 			Case "JOIN"
 				If message.fromUser=server.nickname Then
+					UpdateHistory()
 					container.AddMessage("You are now talking in "+container.name)
 				Else
-					container.AddMessage(message.fromUser+" joined "+container.name)
+					container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				Endif
 				
 			Case "PART"
-				If message.text Then
-					container.AddMessage(message.fromUser+" left "+container.name+" (reason: "+message.text+")")
-				Else
-					container.AddMessage(message.fromUser+" left "+container.name)
-				Endif
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				
 			Case "QUIT"
-				If message.text Then
-					container.AddMessage(message.fromUser+" quit (reason: "+message.text+")")
-				Else
-					container.AddMessage(message.fromUser+" quit")
-				Endif
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				
 			Case "NICK"
-				If nicknameLabel.Text.Left(nicknameLabel.Text.Length-1) Then
-					container.AddMessage("You are now known as "+message.toUser)
-				Else
-					container.AddMessage(message.fromUser+" is now known as "+message.toUser)
+				Local wasSelf:Bool
+				If container=server And selectedMessageContainer Then
+					wasSelf=true
+					container=selectedMessageContainer
 				Endif
 				
+				container.AddMessage( message.text, message.fromUser, message.toUser, message.type)
+				
+				If wasSelf Then UpdateUsers()
+				
 			Case "PRIVMSG"
-				container.AddMessage(message.fromUser+": "+message.text)
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				doNotify=True
 			
 			Case "NOTICE"
-				container.AddMessage("NOTICE >"+message.fromUser+"<: "+message.text)
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 			
 			Default
-				container.AddMessage(message.fromUser+": "+message.text)
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				doNotify=True
 		End
 		
 		'Display message if we're in that container right now
 		If container=selectedMessageContainer Then
 			AddChatMessage(container.messages.Last)
-		Elseif doNotify Then 'Notify user perhaps?
+		Elseif doNotify Then
 			Local node:TreeView.Node=GetMessageContainerNode(container.name,server.name)
 			If node Then node.Icon=App.Theme.OpenImage("irc/notice.png")
 		Endif
@@ -1095,14 +1236,68 @@ Class IRCView Extends DockingView
 	
 	Method UpdateHistory()
 		historyField.Clear()
+		
 		If Not selectedMessageContainer Then Return
+		
+		'Limit message count
+		While selectedMessageContainer.messages.Count()>maxHistory
+			selectedMessageContainer.messages.Remove(selectedMessageContainer.messages.First)
+		Wend
+		
 		For Local m:=Eachin selectedMessageContainer.messages
 			AddChatMessage(m)
 		Next
 	End
 	
+	Function PadTime:String(timeStr:String)
+		If Not timeStr.Contains(":") Return timeStr
+		
+		Local t:=timeStr.Split(":")
+		If t[0].Length<=1 Then t[0]="0"+t[0]
+		If t[1].Length<=1 Then t[1]="0"+t[1]
+		
+		Return t[0]+":"+t[1]
+	End
+	
 	Method AddChatMessage(message:IRCMessage)
-		historyField.AppendText("["+message.time+"]~t"+message.text+"~n")
+		Local time:String="["+PadTime(message.time)+"]~t"
+		
+		Select message.type.ToUpper()
+			
+			Case "JOIN"
+				historyField.AppendText( time + message.fromUser + " joined " + message.toUser + "~n" )
+				
+			Case "PART"
+				If message.text Then
+					historyField.AppendText( time + message.fromUser + " left " + message.toUser + " (Reason " + message.text + ")~n" )
+				Else
+					historyField.AppendText( time + message.fromUser + " left " + message.toUser + "~n" )
+				Endif
+				
+			Case "QUIT"
+				If message.text Then
+					historyField.AppendText( time + message.fromUser + " quit (Reason '" + message.text + "')~n" )
+				Else
+					historyField.AppendText( time + message.fromUser + " quit~n" )
+				Endif
+				
+			Case "NICK"
+				historyField.AppendText( time + message.fromUser + " is now known as " + message.text + "~n" )
+				
+			Case "MODE"
+				'historyField.AppendText( time + message.fromUser + " sets MODE " + message.text + "~n" )
+				
+			Case "NOTICE"
+				historyField.AppendText( time + message.fromUser + ": <NOTICE> " + message.text + "~n" )
+				
+			Default
+				If message.fromUser Then
+					historyField.AppendText( time + message.fromUser + ": " + message.text + "~n" )
+				Else
+					historyField.AppendText( time + message.text + "~n" )
+				Endif
+		End
+		
 	End
 	
 	Method OnRender(canvas:Canvas) Override