BOOL CALLBACK playlists_dropdown::preferences::DisplayDlgProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp) {
	switch (msg)
	{
	case MSG_UPDATEPREVIEW:
		{
			static_api_ptr_t<playlist_manager> pm;
			t_size playlist = pm->get_active_playlist();
			if (playlist != pfc_infinite) {
				static_api_ptr_t<titleformat_compiler> cmp;
				titleformat_object::ptr title_format;
				if (cmp->compile(title_format, cfg::title_string)) {
					titleformat_hook_impl_splitter hook(titleformat_hook_impl_splitter(
						&title_format_hook(playlist, false),
						&titleformat_hook_impl_list(playlist, pm->get_playlist_count())));
					pfc::string8 title;
					title_format->run(&hook, title, NULL);
					pfc::string8 preview;
					cmp->remove_color_marks(title, preview);
					preview.replace_char('\t', ' ');
					uSetDlgItemText(wnd, IDC_PREVIEW, preview);
				} else {
					uSetDlgItemText(wnd, IDC_PREVIEW, "COMPILATION ERROR");
				}
			} else {
				uSetDlgItemText(wnd, IDC_PREVIEW, "N/A");
			}
		}
		break;

	case WM_COMMAND:
		switch (wp)
		{
			// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
			//  GENERAL
			// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		case IDC_SHOW_ICONS:
			cfg::show_icons = uButton_GetCheck(wnd, IDC_SHOW_ICONS);
			playlists_dropdown::g_reload_icons();
			playlists_dropdown::g_update_all_sizes();
			playlists_dropdown::g_redraw_all();
			break;

		case IDC_ICON_UTILS:
			{
				HMENU menu = CreatePopupMenu();
				uAppendMenu(menu, MF_STRING, 1, "Clear custom icons history");
				uAppendMenu(menu, MF_STRING, 2, "Reset all icons to default");
				RECT rc;
				GetWindowRect(GetDlgItem(wnd, IDC_ICON_UTILS), &rc);
				int cmd = TrackPopupMenu(menu, TPM_NONOTIFY | TPM_RETURNCMD, rc.left, rc.bottom, 0, GetDlgItem(wnd, IDC_ICON_UTILS), 0);
				DestroyMenu(menu);

				if (cmd == 1 && uMessageBox(wnd, "Are you sure you want to clear all custom icons history?", "Continue?", MB_YESNO) == IDYES) {
					cfg::custom_icons_history.reset();
				} else if (cmd == 2 && uMessageBox(wnd, "Are you sure you want to reset all icons to default?", "Continue?", MB_YESNO) == IDYES) {
					try {
						static_api_ptr_t<playlist_manager_v2> pm2;
						for (int i = 0, total = pm2->get_playlist_count(); i < total; i++) {
							pm2->playlist_remove_property(i, guid_icon_path);
						}
					} catch(...) { }
				}

				playlists_dropdown::g_reload_icons();
				playlists_dropdown::g_update_all_sizes();
				playlists_dropdown::g_redraw_all();
			}
			break;

		case IDC_MIN_VISIBLE | EN_CHANGE << 16:
			cfg::min_visible = uGetDlgItemInt(wnd, IDC_MIN_VISIBLE, NULL, true);
			playlists_dropdown::g_update_all_min_visible();
			break;

		case IDC_FORMATTING_STRING | EN_CHANGE << 16:
			if (TabCtrl_GetDlgCurSel(wnd, IDC_FS_TAB) == 0) {
				uGetDlgItemText(wnd, IDC_FORMATTING_STRING, cfg::title_string);
				uSendMessage(wnd, MSG_UPDATEPREVIEW, 0, 0);
			} else {
				uGetDlgItemText(wnd, IDC_FORMATTING_STRING, cfg::style_string);
			}
			uEnableWindow(uGetDlgItem(wnd, IDC_APPLY), true);
			break;

			// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
			//  PADDING
			// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		case IDC_PADDING_TOP | EN_CHANGE << 16:
			cfg::padding.get_value().top = uGetDlgItemInt(wnd, IDC_PADDING_TOP, NULL, true);
			playlists_dropdown::g_update_all_sizes();
			playlists_dropdown::g_redraw_all();
			break;

		case IDC_PADDING_RIGHT | EN_CHANGE << 16:
			cfg::padding.get_value().right = uGetDlgItemInt(wnd, IDC_PADDING_RIGHT, NULL, true);
			playlists_dropdown::g_update_all_sizes();
			playlists_dropdown::g_redraw_all();
			break;

		case IDC_PADDING_BOTTOM | EN_CHANGE << 16:
			cfg::padding.get_value().bottom = uGetDlgItemInt(wnd, IDC_PADDING_BOTTOM, NULL, true);
			playlists_dropdown::g_update_all_sizes();
			playlists_dropdown::g_redraw_all();
			break;

		case IDC_PADDING_LEFT | EN_CHANGE << 16:
			cfg::padding.get_value().left = uGetDlgItemInt(wnd, IDC_PADDING_LEFT, NULL, true);
			playlists_dropdown::g_update_all_sizes();
			playlists_dropdown::g_redraw_all();
			break;

			// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
			//  FIELDS BUTTON
			// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		case IDC_FIELDS:
			{
				HMENU menu = CreatePopupMenu();
				for (int i = 1; i < tabsize(g_fields_list); i++) {
					uAppendMenu(menu, !!g_fields_list[i].label ? MF_STRING : MF_SEPARATOR, i, g_fields_list[i].label);
				}
				RECT rc;
				GetWindowRect(GetDlgItem(wnd, IDC_FIELDS), &rc);
				int cmd = TrackPopupMenu(menu, TPM_NONOTIFY | TPM_RETURNCMD, rc.left, rc.bottom, 0, GetDlgItem(wnd, IDC_FIELDS), 0);
				DestroyMenu(menu);
				if (cmd >= 0 && cmd < tabsize(g_fields_list) && !!g_fields_list[cmd].label) {
					uSendDlgItemMessageText(wnd, IDC_FORMATTING_STRING, EM_REPLACESEL, TRUE, g_fields_list[cmd].label);
					SetFocus(GetDlgItem(wnd, IDC_FORMATTING_STRING));
				}
			}
			break;

			// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
			//  APPLY
			// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		case IDC_APPLY:
			apply_config();
			uEnableWindow(uGetDlgItem(wnd, IDC_APPLY), FALSE);
			break;
		}
		break;

	case WM_INITDIALOG:
		{
			EnableTheming(wnd);

			// Setup "Display/Style" TabControl
			HWND hTabWnd = uGetDlgItem(wnd, IDC_FS_TAB);
			uTabCtrl_InsertItemText(hTabWnd, 0, "Display");
			uTabCtrl_InsertItemText(hTabWnd, 1, "Style");
			TabCtrl_SetCurSel(hTabWnd, cfg::last_fs_tab);
			uSetDlgItemText(wnd, IDC_FORMATTING_STRING, get_formatting_string(TabCtrl_GetCurSel(hTabWnd)));

			uButton_SetCheck(wnd, IDC_SHOW_ICONS, cfg::show_icons);

			SetDlgSpinner(wnd, IDC_MIN_VISIBLE_SPIN, IDC_MIN_VISIBLE, 1, 1000, cfg::min_visible);
			SetDlgSpinner(wnd, IDC_PADDING_TOP_SPIN, IDC_PADDING_TOP, 0, 1024, cfg::padding.get_value().top);
			SetDlgSpinner(wnd, IDC_PADDING_RIGHT_SPIN, IDC_PADDING_RIGHT, 0, 1024, cfg::padding.get_value().right);
			SetDlgSpinner(wnd, IDC_PADDING_BOTTOM_SPIN, IDC_PADDING_BOTTOM, 0, 1024, cfg::padding.get_value().bottom);
			SetDlgSpinner(wnd, IDC_PADDING_LEFT_SPIN, IDC_PADDING_LEFT, 0, 1024, cfg::padding.get_value().left);

			uEnableWindow(uGetDlgItem(wnd, IDC_APPLY), FALSE);
			SendMessage(wnd, MSG_UPDATEPREVIEW, 0, 0);
		}
		break;

	case WM_DESTROY:
		cfg::last_fs_tab = TabCtrl_GetDlgCurSel(wnd, IDC_FS_TAB);
		break;

	case WM_NOTIFY:
		if (((LPNMHDR) lp)->idFrom == IDC_FS_TAB && ((LPNMHDR) lp)->code == TCN_SELCHANGE) {
			uSetDlgItemText(wnd, IDC_FORMATTING_STRING, get_formatting_string(TabCtrl_GetDlgCurSel(wnd, IDC_FS_TAB)));
		}
		break;

	default:
		return false;
	}
	return true;
}
void speedtest(column_list_cref_t columns, bool b_global, bool b_legacy, bool b_date)
{
	static_api_ptr_t<playlist_manager> playlist_api;
	static_api_ptr_t<titleformat_compiler> titleformat_api;
	service_ptr_t<genrand_service> p_genrand;
	p_genrand = genrand_service::g_create();

	unsigned activeplaylist_item_count = playlist_api->activeplaylist_get_item_count();

	bool b_global_colour_used = false;
	bool b_column_times_valid = false;

	unsigned column_count = columns.get_count();
	pfc::array_t<column_times> times_columns;
	times_columns.set_size(column_count);

	{
		unsigned i; 
		for (i=0; i<column_count; i++)
			if (!columns[i]->use_custom_colour)
			{
				b_global_colour_used = true;
				break;
			}
	}


	global_variable_list p_vars;

	double time_compile_global = 0, time_compile_global_colour = 0, time_global = 0, time_colour = 0;

	service_ptr_t<titleformat_object> to_global;
	service_ptr_t<titleformat_object> to_global_colour;

	{
		double time_temp = 0;
		unsigned i;
		if (b_global && column_count)
			for (i=0; i<10; i++)
			{
				to_global.release();
				pfc::hires_timer timer;
				timer.start();
				titleformat_api->compile_safe(to_global, cfg_globalstring);
				time_temp += timer.query();
			}
			time_compile_global = time_temp / 10;
	}

	{
		double time_temp = 0;
		unsigned i;
		if (b_global_colour_used)
			for (i=0; i<10; i++)
			{
				to_global_colour.release();
				pfc::hires_timer timer;
				timer.start();
				titleformat_api->compile_safe(to_global_colour, cfg_colour);
				time_temp += timer.query();
			}
			time_compile_global_colour = time_temp / 10;
	}

	pfc::array_t<unsigned> tracks;
	tracks.set_size(16);

	if (activeplaylist_item_count >= 1)
	{

		b_column_times_valid = true;

		{
			unsigned i;
			for (i=0; i<16; i++)
				tracks[i] = p_genrand->genrand(activeplaylist_item_count);
		}

		SYSTEMTIME st;
		if (b_date) GetLocalTime(&st);

		{
			pfc::string8_fast_aggressive str_temp;
			str_temp.prealloc(512);

			{
				double time_temp = 0;
				unsigned i;
				if (b_global && column_count)
					for (i=0; i<10; i++)
					{
						unsigned j;
						for (j=0;j<16;j++)
						{
							p_vars.delete_all();
							pfc::hires_timer timer;
							timer.start();
							{
								if (b_date)
								{
									playlist_api->activeplaylist_item_format_title(tracks[j], &titleformat_hook_impl_splitter(&titleformat_hook_set_global<true,false>(p_vars,b_legacy), b_date ? &titleformat_hook_date(&st) : 0), str_temp, to_global, 0, play_control::display_level_all);
								}
								else
								{
									playlist_api->activeplaylist_item_format_title(tracks[j], &titleformat_hook_set_global<true,false>(p_vars,b_legacy), str_temp, to_global, 0, play_control::display_level_all);
								}


								//	if (map_codes) extra_formatted.replace_char(3, 6);

								if (b_legacy)
								{
									const char * ptr = str_temp;

									const char * start = ptr;

									while(*ptr)
									{
										start = ptr;
										while (*ptr && *ptr != '\x07') ptr++;
										if (ptr > start)
										{
											const char * p_equal = strchr_n(start+1, '=', ptr-start-1);
											if (p_equal)
											{
												p_vars.add_item(start,p_equal-start,p_equal+1,ptr-p_equal-1);
											}
										}
										while (*ptr && *ptr == '\x07') ptr++;
									}
								}
							}
							time_temp += timer.query();
						}
					}
					time_global = time_temp / (10 * 16);
			}

			{
				double time_temp = 0;
				unsigned i;
				if (b_global_colour_used)
					for (i=0; i<10; i++)
					{
						unsigned j;
						for (j=0;j<16;j++)
						{
							colourinfo col_item(0,0,0,0,0,0);
							pfc::hires_timer timer;
							timer.start();
							playlist_api->activeplaylist_item_format_title(tracks[j], &titleformat_hook_splitter_pt3(&titleformat_hook_style(col_item),b_global ? &titleformat_hook_set_global<false,true>(p_vars, b_legacy) : 0,b_date ? &titleformat_hook_date(&st) : 0), str_temp, to_global_colour, 0, play_control::display_level_all);
							time_temp += timer.query();
						}
					}
					time_colour = time_temp / (10 * 16);
			}


			unsigned n;
			for (n=0; n<column_count; n++)
			{
				{
					double time_temp = 0;
					unsigned i;
					for (i=0; i<10; i++)
					{
						times_columns[n].to_display.release();
						pfc::hires_timer timer;
						timer.start();
						titleformat_api->compile_safe(times_columns[n].to_display, columns[n]->spec);
						time_temp += timer.query();
					}
					times_columns[n].time_display_compile = time_temp / 10;
					time_temp=0;
					for (i=0; i<10; i++)
					{
						unsigned j;
						for (j=0;j<16;j++)
						{
							pfc::hires_timer timer;
							timer.start();
							playlist_api->activeplaylist_item_format_title(tracks[j], &titleformat_hook_impl_splitter( b_global ? &titleformat_hook_set_global<false,true>(p_vars, b_legacy) :0, b_date ? &titleformat_hook_date(&st) : 0), str_temp, times_columns[n].to_display, 0, play_control::display_level_all);
							time_temp += timer.query();
						}
					}
					times_columns[n].time_display = time_temp / (10  * 16);
				}
				{
					times_columns[n].time_colour_compile = 0;
					times_columns[n].time_colour = 0;

					if (columns[n]->use_custom_colour)
					{
						double time_temp = 0;
						unsigned i;
						for (i=0; i<10; i++)
						{
							times_columns[n].to_colour.release();
							pfc::hires_timer timer;
							timer.start();
							titleformat_api->compile_safe(times_columns[n].to_colour, columns[n]->colour_spec);
							time_temp += timer.query();
						}
						times_columns[n].time_colour_compile = time_temp / 10;
						time_temp=0;
						for (i=0; i<10; i++)
						{
							unsigned j;
							for (j=0;j<16;j++)
							{
								colourinfo col_item(0,0,0,0,0,0);
								pfc::hires_timer timer;
								timer.start();
								playlist_api->activeplaylist_item_format_title(tracks[j], &titleformat_hook_splitter_pt3(&titleformat_hook_style(col_item),b_global ? &titleformat_hook_set_global<false,true>(p_vars, b_legacy) : 0,b_date ? &titleformat_hook_date(&st) : 0), str_temp, times_columns[n].to_colour, 0, play_control::display_level_all);
								time_temp += timer.query();
							}
						}
						times_columns[n].time_colour = time_temp / (10  * 16);
					}
				}
			}
		}
	}

	{
		pfc::string8_fast_aggressive buffer;
		buffer.prealloc(512);
		buffer += "script compile tests\n========\n";
		buffer += "(track)\x09\x09""variable\x09""style";
		buffer += "\n\x9\x9";
		double_to_string(time_compile_global, buffer);
		buffer += "\x9";
		double_to_string(time_compile_global_colour, buffer);

		if (b_column_times_valid)
		{
			unsigned n;

			buffer += "\n\n(column)\x09""display\x09\x09""style";

			for (n=0; n<column_count; n++)
			{
				buffer += "\ncolumn ";
				buffer.add_string(pfc::format_int(n+1));
				buffer.add_byte('\x9');
				double_to_string(times_columns[n].time_display_compile, buffer);
				buffer.add_byte('\x9');
				double_to_string(times_columns[n].time_colour_compile, buffer);
			}

			buffer += "\n\nper-track script execution tests\n========\n";
			buffer += "tracks chosen to test\n";

			for (n=0; n<16; n++)
			{
				if (n) buffer.add_string(", ");
				buffer.add_string(pfc::format_int(tracks[n]));
			}

			buffer += "\n\n(track)\x09\x09""variable\x09""style";
			buffer += "\n\x9\x9";
			double_to_string(time_global, buffer);
			buffer += "\x9";
			double_to_string(time_colour, buffer);

			buffer += "\n\n(column)\x9""display\x9\x9""style";

			for (n=0; n<column_count; n++)
			{
				buffer += "\ncolumn ";
				buffer.add_string(pfc::format_int((n+1)));
				buffer.add_byte('\x9');
				double_to_string(times_columns[n].time_display, buffer);
				buffer.add_byte('\x9');
				double_to_string(times_columns[n].time_colour, buffer);
			}

			buffer += "\n\nnotes\n========\nUnits measured in seconds.\nScipts that are never executed during use will show 0 seconds for its compilation and execution time.";

		}

		popup_message::g_show(buffer, "speedtest results");
	}
}