text: function on_colours_changed() {
  	panel.colours_changed();
  	window.Repaint();
  }
  
  function on_font_changed() {
  	panel.font_changed();
  	window.Repaint();
  }
  
  function on_playlist_switch() {
  	panel.item_focus_change();
  }
  
  function on_playback_new_track() {
  	panel.item_focus_change();
  }
  
  function on_playback_dynamic_info_track() {
  	panel.item_focus_change();
  }
  
  function on_playback_stop(reason) {
  	if (reason != 2) {
  		panel.item_focus_change();
  	}
  }
  
  function on_item_focus_change() {
  	panel.item_focus_change();
  }
  
  _.mixin({
  	panel : function () {
  		this.item_focus_change = function () {
  			if (this.metadb_func) {
  				if (this.selection.value == 0) {
  					this.metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
  				} else {
  					this.metadb = fb.GetFocusItem();
  				}
  				on_metadb_changed();
  				if (!this.metadb) {
  					_.tt('');
  				}
  			}
  		}
  		
  		this.colours_changed = function () {
  			if (window.InstanceType) {
  				this.colours.background = window.GetColourDUI(1);
  				this.colours.text = window.GetColourDUI(0);
  				this.colours.highlight = window.GetColourDUI(2);
  			} else {
  				this.colours.background = window.GetColourCUI(3);
  				this.colours.text = window.GetColourCUI(0);
  				this.colours.highlight = _.blendColours(this.colours.text, this.colours.background, 0.4);
  			}
  			this.colours.header = this.colours.highlight & 0x45FFFFFF;
  		}
  		
  		this.font_changed = function () {
  			var name;
  			var font = window.InstanceType ? window.GetFontDUI(0) : window.GetFontCUI(0);
  			if (font) {
  				name = font.Name;
  				_.dispose(font);
  			} else {
  				name = 'Segoe UI';
  				console.log(N, 'Unable to use default font. Using', name, 'instead.');
  			}
  			_.dispose(this.fonts.title, this.fonts.normal, this.fonts.fixed);
  			this.fonts.title = _.gdiFont(name, 12, 1);
  			this.fonts.normal = _.gdiFont(name, this.fonts.size.value);
  			this.fonts.fixed = _.gdiFont('Lucida Console', this.fonts.size.value);
  			this.row_height = this.fonts.normal.Height;
  			_.invoke(this.list_objects, 'size');
  			_.invoke(this.list_objects, 'update');
  			_.invoke(this.text_objects, 'size');
  		}
  		
  		this.size = function () {
  			this.w = window.Width;
  			this.h = window.Height;
  		}
  		
  		this.paint = function (gr) {
  			switch (true) {
  			case window.IsTransparent:
  				return;
  			case !this.custom_background:
  			case this.colours.mode.value == 0:
  				var col = this.colours.background;
  				break;
  			case this.colours.mode.value == 1:
  				var col = utils.GetSysColour(15);
  				break;
  			case this.colours.mode.value == 2:
  				var col = this.colours.custom_background.value;
  				break;
  			}
  			gr.FillSolidRect(0, 0, this.w, this.h, col);
  		}
  		
  		this.rbtn_up = function (x, y, object) {
  			this.m = window.CreatePopupMenu();
  			this.s1 = window.CreatePopupMenu();
  			this.s2 = window.CreatePopupMenu();
  			this.s3 = window.CreatePopupMenu();
  			this.s10 = window.CreatePopupMenu();
  			this.s11 = window.CreatePopupMenu();
  			this.s12 = window.CreatePopupMenu();
  			this.s13 = window.CreatePopupMenu();
  			// panel 1-999
  			// object 1000+
  			if (object) {
  				object.rbtn_up(x, y);
  			}
  			if (this.list_objects.length || this.text_objects.length) {
  				_.forEach(this.fonts.sizes, function (item) {
  					this.s1.AppendMenuItem(MF_STRING, item, item);
  				}, this);
  				this.s1.CheckMenuRadioItem(_.first(this.fonts.sizes), _.last(this.fonts.sizes), this.fonts.size.value);
  				this.s1.AppendTo(this.m, MF_STRING, 'Font size');
  				this.m.AppendMenuSeparator();
  			}
  			if (this.custom_background) {
  				this.s2.AppendMenuItem(MF_STRING, 100, window.InstanceType ? 'Use default UI setting' : 'Use columns UI setting');
  				this.s2.AppendMenuItem(MF_STRING, 101, 'Splitter');
  				this.s2.AppendMenuItem(MF_STRING, 102, 'Custom');
  				this.s2.CheckMenuRadioItem(100, 102, this.colours.mode.value + 100);
  				this.s2.AppendMenuSeparator();
  				this.s2.AppendMenuItem(this.colours.mode.value == 2 ? MF_STRING : MF_GRAYED, 103, 'Set custom colour...');
  				this.s2.AppendTo(this.m, window.IsTransparent ? MF_GRAYED : MF_STRING, 'Background');
  				this.m.AppendMenuSeparator();
  			}
  			if (this.metadb_func) {
  				this.s3.AppendMenuItem(MF_STRING, 110, 'Prefer now playing');
  				this.s3.AppendMenuItem(MF_STRING, 111, 'Follow selected track (playlist)');
  				this.s3.CheckMenuRadioItem(110, 111, this.selection.value + 110);
  				this.s3.AppendTo(this.m, MF_STRING, 'Selection mode');
  				this.m.AppendMenuSeparator();
  			}
  			this.m.AppendMenuItem(MF_STRING, 120, 'Configure...');
  			var idx = this.m.TrackPopupMenu(x, y);
  			switch (true) {
  			case idx == 0:
  				break;
  			case idx <= 20:
  				this.fonts.size.set(idx);
  				on_font_changed();
  				break;
  			case idx == 100:
  			case idx == 101:
  			case idx == 102:
  				this.colours.mode.set(idx - 100);
  				window.Repaint();
  				break;
  			case idx == 103:
  				this.colours.custom_background.set(utils.ColourPicker(window.ID, this.colours.custom_background.value));
  				window.Repaint();
  				break;
  			case idx == 110:
  			case idx == 111:
  				this.selection.set(idx - 110);
  				this.item_focus_change();
  				break;
  			case idx == 120:
  				window.ShowConfigure();
  				break;
  			case idx > 999:
  				if (object) {
  					object.rbtn_up_done(idx);
  				}
  				break;
  			}
  			_.dispose(this.m, this.s1, this.s2, this.s3, this.s10, this.s11, this.s12, this.s13);
  			return true;
  		}
  		
  		this.tf = function (t) {
  			if (!this.metadb) {
  				return '';
  			}
  			if (!this.tfo[t]) {
  				this.tfo[t] = fb.TitleFormat(t);
  			}
  			var path = this.tfo['$if2(%__@%,%path%)'].EvalWithMetadb(this.metadb);
  			if (fb.IsPlaying && (_.startsWith(path, 'http') || _.startsWith(path, 'mms'))) {
  				return this.tfo[t].Eval();
  			} else {
  				return this.tfo[t].EvalWithMetadb(this.metadb);
  			}
  		}
  		
  		this.fonts = {};
  		this.colours = {};
  		this.w = 0;
  		this.h = 0;
  		this.metadb = fb.GetFocusItem();
  		this.metadb_func = typeof on_metadb_changed == 'function';
  		this.fonts.sizes = [10, 12, 14, 16];
  		this.fonts.size = new _.p('2K3.PANEL.FONTS.SIZE', 12);
  		if (this.metadb_func) {
  			this.selection = new _.p('2K3.PANEL.SELECTION', 0);
  		}
  		switch (true) {
  			case arguments.length == 2 && _.indexOf(arguments[1], 'custom_background') > -1:
  			case arguments[0] == 'custom_background':
  				this.custom_background = true;
  				this.colours.mode = new _.p('2K3.PANEL.COLOURS.MODE', 0);
  				this.colours.custom_background = new _.p('2K3.PANEL.COLOURS.CUSTOM.BACKGROUND', _.RGB(0, 0, 0));
  				break;
  			default:
  				this.custom_background = false;
  				break;
  		}
  		this.list_objects = [];
  		this.text_objects = [];
  		this.tfo = {
  			'$if2(%__@%,%path%)' : fb.TitleFormat('$if2(%__@%,%path%)')
  		};
  		this.font_changed();
  		this.colours_changed();
  	}
  });
  