<?php

###############################################################################
#
# Controls Factory Extended
#
# Author: Brigadir (forum.mydune.ru)
# Date: 04-11-2018
# Latest update: 30-01-2021
#
###############################################################################


require_once 'lib/control_factory.php';

///////////////////////////////////////////////////////////////////////////////

class ControlFactoryExt extends ControlFactory
{
	///////////////////////////////////////////////////////////////////////////

	const	DUNE_BASE_SKIN_PATH				=	'/firmware/skin';

	# Paths to skin cut images
	const	scrollbar_inner_txt				=	'gui_skin://cut_images/scrollbar_inner/scrollbar_inner.txt';
	const	scrollbar_inner_bottom			=	'gui_skin://cut_images/scrollbar_inner/scrollbar_inner_bottom.';
	const	scrollbar_inner_center			=	'gui_skin://cut_images/scrollbar_inner/scrollbar_inner_center.';
	const	scrollbar_inner_top				=	'gui_skin://cut_images/scrollbar_inner/scrollbar_inner_top.';
	const	scrollbar_outer_txt				=	'gui_skin://cut_images/scrollbar_outer/scrollbar_outer.txt';
	const	scrollbar_outer_bottom			=	'gui_skin://cut_images/scrollbar_outer/scrollbar_outer_bottom.';
	const	scrollbar_outer_center			=	'gui_skin://cut_images/scrollbar_outer/scrollbar_outer_center.';
	const	scrollbar_outer_top				=	'gui_skin://cut_images/scrollbar_outer/scrollbar_outer_top.';
	const	progressbar_inner_txt			=	'gui_skin://cut_images/progressbar_inner/progressbar_inner.txt';
	const	progressbar_inner_center		=	'gui_skin://cut_images/progressbar_inner/progressbar_inner_center.';
	const	progressbar_inner_left			=	'gui_skin://cut_images/progressbar_inner/progressbar_inner_left.';
	const	progressbar_inner_right			=	'gui_skin://cut_images/progressbar_inner/progressbar_inner_right.';
	const	progressbar_outer_txt			=	'gui_skin://cut_images/progressbar_outer/progressbar_outer.txt';
	const	progressbar_outer_center		=	'gui_skin://cut_images/progressbar_outer/progressbar_outer_center.';
	const	progressbar_outer_left			=	'gui_skin://cut_images/progressbar_outer/progressbar_outer_left.';
	const	progressbar_outer_right			=	'gui_skin://cut_images/progressbar_outer/progressbar_outer_right.';
	const	dialog_background_txt			=	'gui_skin://cut_images/dialog_background/dialog_background.txt';
	const	dialog_background_center		=	'gui_skin://cut_images/dialog_background/dialog_background_center.';
	const	sandwich_cover_txt				=	'gui_skin://cut_images/sandwich_cover/sandwich_cover.txt';
	const	sandwich_cover_left				=	'gui_skin://cut_images/sandwich_cover/sandwich_cover_left.';
	const	sandwich_cover_right			=	'gui_skin://cut_images/sandwich_cover/sandwich_cover_right.';
	const	sandwich_cover_top				=	'gui_skin://cut_images/sandwich_cover/sandwich_cover_top.';
	const	sandwich_cover_bottom			=	'gui_skin://cut_images/sandwich_cover/sandwich_cover_bottom.';
	const	sandwich_cover_top_left			=	'gui_skin://cut_images/sandwich_cover/sandwich_cover_top_left.';
	const	sandwich_cover_top_right		=	'gui_skin://cut_images/sandwich_cover/sandwich_cover_top_right.';
	const	sandwich_cover_bottom_left		=	'gui_skin://cut_images/sandwich_cover/sandwich_cover_bottom_left.';
	const	sandwich_cover_bottom_right		=	'gui_skin://cut_images/sandwich_cover/sandwich_cover_bottom_right.';
	const	sandwich_mask_txt				=	'gui_skin://cut_images/sandwich_mask/sandwich_mask.txt';

	///////////////////////////////////////////////////////////////////////////

	private static	$instance = null;
	private static	$sys_settings_ini;

	///////////////////////////////////////////////////////////////////////////

	private		$dots;
	private		$skin_path;

	# Arrays of cut images manifest
	protected	$scrollbar_inner_manifest;
	protected	$scrollbar_outer_manifest;
	protected	$progressbar_inner_manifest;
	protected	$progressbar_outer_manifest;
	protected	$dialog_background_manifest;
	protected	$sandwich_cover_manifest;
	protected	$sandwich_mask_manifest;

	protected	$dialog_background_center;
	protected	$sandwich_cover_center;

	///////////////////////////////////////////////////////////////////////////

	private function __construct()
	{}

	///////////////////////////////////////////////////////////////////////////

	public static function init()
	{

		if (is_null(self::$instance))
			self::$instance = new ControlFactoryExt();

		clearstatcache();

		self::$instance->skin_path = get_active_skin_path();
		self::$sys_settings_ini = get_shell_settings();

		# Paths to specific dir
		$scrollbar_inner_path = file_exists(str_replace('gui_skin:/', self::$instance->skin_path, self::scrollbar_inner_txt))? self::$instance->skin_path : self::DUNE_BASE_SKIN_PATH;
		$scrollbar_outer_path = file_exists(str_replace('gui_skin:/', self::$instance->skin_path, self::scrollbar_outer_txt))? self::$instance->skin_path : self::DUNE_BASE_SKIN_PATH;
		$progressbar_inner_path = file_exists(str_replace('gui_skin:/', self::$instance->skin_path, self::progressbar_inner_txt))? self::$instance->skin_path : self::DUNE_BASE_SKIN_PATH;
		$progressbar_outer_path = file_exists(str_replace('gui_skin:/', self::$instance->skin_path, self::progressbar_outer_txt))? self::$instance->skin_path : self::DUNE_BASE_SKIN_PATH;
		$dialog_background_path = file_exists(str_replace('gui_skin:/', self::$instance->skin_path, self::dialog_background_txt))? self::$instance->skin_path : self::DUNE_BASE_SKIN_PATH;
		$sandwich_cover_path = file_exists(str_replace('gui_skin:/', self::$instance->skin_path, self::sandwich_cover_txt))? self::$instance->skin_path : self::DUNE_BASE_SKIN_PATH;
		$sandwich_mask_path = file_exists(str_replace('gui_skin:/', self::$instance->skin_path, self::sandwich_mask_txt))? self::$instance->skin_path : self::DUNE_BASE_SKIN_PATH;

		# Arrays of cut image manifest
		self::$instance->scrollbar_inner_manifest = parse_ini_file(str_replace('gui_skin:/', $scrollbar_inner_path, self::scrollbar_inner_txt));
		self::$instance->scrollbar_outer_manifest = parse_ini_file(str_replace('gui_skin:/', $scrollbar_outer_path, self::scrollbar_outer_txt));
		self::$instance->progressbar_inner_manifest = parse_ini_file(str_replace('gui_skin:/', $progressbar_inner_path, self::progressbar_inner_txt));
		self::$instance->progressbar_outer_manifest = parse_ini_file(str_replace('gui_skin:/', $progressbar_outer_path, self::progressbar_outer_txt));
		self::$instance->dialog_background_manifest = parse_ini_file(str_replace('gui_skin:/', $dialog_background_path, self::dialog_background_txt));
		self::$instance->sandwich_cover_manifest = parse_ini_file(str_replace('gui_skin:/', $sandwich_cover_path, self::sandwich_cover_txt));
		self::$instance->sandwich_mask_manifest = parse_ini_file(str_replace('gui_skin:/', $sandwich_mask_path, self::sandwich_mask_txt));

		$dots_path = get_paved_path(DuneSystem::$properties['tmp_dir_path'] . '/ex_controls/dots/');

		if (isset(self::$instance->dialog_background_manifest['center_color']))
		{
			$center_color = str_replace('#', '0', self::$instance->dialog_background_manifest['center_color']);
			self::$instance->dialog_background_center = $dots_path . "/$center_color.aai";

			if (!file_exists(self::$instance->dialog_background_center))
			{
				$argb = str_split($center_color, 2);

				if (false === file_put_contents(self::$instance->dialog_background_center, pack("V2C4", 1, 1, hexdec($argb[4]), hexdec($argb[3]), hexdec($argb[2]), hexdec($argb[1]))))
					throw new Exception(get_class(self::$instance).': Attempt to write to the system drive failed!');
			}
		}
		else
			if (isset(self::$instance->dialog_background_manifest['has_center_icon']))
				if (self::$instance->dialog_background_manifest['has_center_icon'])
					self::$instance->dialog_background_center = "$dialog_background_path/dialog_background_center." . self::$instance->dialog_background_manifest['ext'];

		if (isset(self::$instance->sandwich_mask_manifest['center_color']))
		{
			$center_color = '0x10ffffff';//str_replace('#', '0x', self::$instance->sandwich_mask_manifest['center_color']);//'0x20ffffff';
			self::$instance->sandwich_cover_center = $dots_path . "/$center_color.aai";

			if (!file_exists(self::$instance->sandwich_cover_center))
			{
				$argb = str_split($center_color, 2);

				if (false === file_put_contents(self::$instance->sandwich_cover_center, pack("V2C4", 1, 1, hexdec($argb[4]), hexdec($argb[3]), hexdec($argb[2]), hexdec($argb[1]))))
					throw new Exception(get_class(self::$instance).': Attempt to write to the system drive failed!');
			}
		}

		$indexed_colors_map = array_merge(array('0x5effffff'), func_get_args());

		foreach ($indexed_colors_map as $idx => $argb_color)
			if (preg_match('/0x[0-9|a-f]{8,8}$/i', $argb_color))
			{
				$argb = str_split($argb_color, 2);
				self::$instance->dots[$idx] = $dots_path . "/$argb_color.aai";

				if (false === file_put_contents(self::$instance->dots[$idx], pack("V2C4", 1, 1, hexdec($argb[4]), hexdec($argb[3]), hexdec($argb[2]), hexdec($argb[1]))))
					throw new Exception(get_class(self::$instance).': Attempt to write to the system drive failed!');
			}
			else
			{
				unset($indexed_colors_map[$idx]);
				hd_print("Warning in " . get_class(self::$instance) . "! Wrong colors map value $argb_color, color index $idx is skiped.");
			}
	}

	///////////////////////////////////////////////////////////////////////////

	public static function add_dialog_patch(&$defs, $dx, $dy, $width = 100)
	{
		if (is_null(self::$instance))
			self::init();

		$dx = max(0, $dx);
		$width = min(1800, max(1, $width));
		$gap = !empty($dx)? "<gap width=$dx/>" : '';
		self::add_smart_label($defs, null, $gap . "<icon dy=$dy width=$width height=69>" . self::$instance->dialog_background_center . '</icon>');
		self::add_vgap($defs, -69);
	}

	public static function add_box(&$defs, $height, $viewport_width)
	{
		if (is_null(self::$instance))
			self::init();

		$viewport_width = max(300, $viewport_width);

		self::add_smart_label($defs, null, '<gap width=5/><icon dy=8 width=' . ($viewport_width - 10) . ' height=' . ($height - 10) . '>' . self::$instance->sandwich_cover_center . '</icon>');
		self::add_vgap($defs, -69);
		self::add_smart_label($defs, null, '<icon>' . self::sandwich_cover_top_left . self::$instance->sandwich_cover_manifest['ext'] . '</icon><icon width=' . ($viewport_width - self::$instance->sandwich_cover_manifest['left'] - self::$instance->sandwich_cover_manifest['right']) . ' height=' . self::$instance->sandwich_cover_manifest['top'] . '>' . self::sandwich_cover_top . self::$instance->sandwich_cover_manifest['ext'] . '</icon><icon>' . self::sandwich_cover_top_right . self::$instance->sandwich_cover_manifest['ext'] . '</icon>');
		self::add_vgap($defs, self::$instance->sandwich_cover_manifest['top'] - 69);
		self::add_smart_label($defs, null, '<icon width=' . self::$instance->sandwich_cover_manifest['left'] . ' height=' . ($height - self::$instance->sandwich_cover_manifest['top'] - self::$instance->sandwich_cover_manifest['bottom']) . '>' . self::sandwich_cover_left . self::$instance->sandwich_cover_manifest['ext'] . '</icon><gap width=' . ($viewport_width - self::$instance->sandwich_cover_manifest['left'] - self::$instance->sandwich_cover_manifest['right']) . '/><icon width=' . self::$instance->sandwich_cover_manifest['right'] . ' height=' . ($height - self::$instance->sandwich_cover_manifest['top'] - self::$instance->sandwich_cover_manifest['bottom']) . '>' . self::sandwich_cover_right . self::$instance->sandwich_cover_manifest['ext'] . '</icon>');
		self::add_vgap($defs, - self::$instance->sandwich_cover_manifest['top'] - self::$instance->sandwich_cover_manifest['bottom'] + $height - 69);
		self::add_smart_label($defs, null, '<icon>' . self::sandwich_cover_bottom_left . self::$instance->sandwich_cover_manifest['ext'] . '</icon><icon width=' . ($viewport_width - self::$instance->sandwich_cover_manifest['left'] - self::$instance->sandwich_cover_manifest['right']) . ' height=' . self::$instance->sandwich_cover_manifest['bottom'] . '>' . self::sandwich_cover_bottom . self::$instance->sandwich_cover_manifest['ext'] . '</icon><icon>' . self::sandwich_cover_bottom_right . self::$instance->sandwich_cover_manifest['ext'] . '</icon>');
		self::add_vgap($defs, self::$instance->sandwich_cover_manifest['bottom'] - $height - 69 + 20);
	}

	public static function add_file_treeview(&$defs, $path, $viewport_width, $max_visible_lines, $drive_icon, $folder_icon, $file_icon)
	{
		if (is_null(self::$instance))
			self::init();

		$height = 0;
		$viewport_height = 314;
		$viewport_width = max(300, $viewport_width);
		$max_visible_lines = min(12, max(4, $max_visible_lines));
		self::add_smart_label($defs, null, '<gap width=5/><icon dy=8 width=' . ($viewport_width - 10) . ' height=' . ($viewport_height - 10) . '>' . self::$instance->sandwich_cover_center . '</icon>');
		self::add_vgap($defs, -69);
		self::add_smart_label($defs, null, '<icon>' . self::sandwich_cover_top_left . self::$instance->sandwich_cover_manifest['ext'] . '</icon><icon width=' . ($viewport_width - self::$instance->sandwich_cover_manifest['left'] - self::$instance->sandwich_cover_manifest['right']) . ' height=' . self::$instance->sandwich_cover_manifest['top'] . '>' . self::sandwich_cover_top . self::$instance->sandwich_cover_manifest['ext'] . '</icon><icon>' . self::sandwich_cover_top_right . self::$instance->sandwich_cover_manifest['ext'] . '</icon>');
		self::add_vgap($defs, self::$instance->sandwich_cover_manifest['top'] - 69);
		self::add_smart_label($defs, null, '<icon width=' . self::$instance->sandwich_cover_manifest['left'] . ' height=' . ($viewport_height - self::$instance->sandwich_cover_manifest['top'] - self::$instance->sandwich_cover_manifest['bottom']) . '>' . self::sandwich_cover_left . self::$instance->sandwich_cover_manifest['ext'] . '</icon><gap width=' . ($viewport_width - self::$instance->sandwich_cover_manifest['left'] - self::$instance->sandwich_cover_manifest['right']) . '/><icon width=' . self::$instance->sandwich_cover_manifest['right'] . ' height=' . ($viewport_height - self::$instance->sandwich_cover_manifest['top'] - self::$instance->sandwich_cover_manifest['bottom']) . '>' . self::sandwich_cover_right . self::$instance->sandwich_cover_manifest['ext'] . '</icon>');
		self::add_vgap($defs, - self::$instance->sandwich_cover_manifest['top'] - self::$instance->sandwich_cover_manifest['bottom'] + $viewport_height - 69);
		self::add_smart_label($defs, null, '<icon>' . self::sandwich_cover_bottom_left . self::$instance->sandwich_cover_manifest['ext'] . '</icon><icon width=' . ($viewport_width - self::$instance->sandwich_cover_manifest['left'] - self::$instance->sandwich_cover_manifest['right']) . ' height=' . self::$instance->sandwich_cover_manifest['bottom'] . '>' . self::sandwich_cover_bottom . self::$instance->sandwich_cover_manifest['ext'] . '</icon><icon>' . self::sandwich_cover_bottom_right . self::$instance->sandwich_cover_manifest['ext'] . '</icon>');
		self::add_vgap($defs, self::$instance->sandwich_cover_manifest['bottom'] - $viewport_height - 69 + 20);
		$path_arr = explode('/', $path);
		$s = $max_visible_lines;
		$c = count($path_arr);
		$path = '';
		$n = 0;

		foreach ($path_arr as $i => $dir)
		{
			if (empty($dir))
				continue;

			if (empty($path))
				self::add_smart_label($defs, null, '<gap width=20/><icon>' . PLUGIN_IMG_PATH . '/drive.png</icon><text size=small> ' . $dir . '</text>');
			else
			{
				if ((($c - $i + intval(!empty($n))) > $max_visible_lines))
				{
					if (empty($n))
					{
						self::add_smart_label($defs, null, '<gap width=50/><icon>' . PLUGIN_IMG_PATH . '/pass.png</icon><text size=small> </text>');
						self::add_vgap($defs, -34);
					}

					$n++;
					$path .= '/' . $dir;
					continue;
				}

				self::add_smart_label($defs, null, '<gap width=' . (20 + 35*($i - $n)) . '/><icon>' . PLUGIN_IMG_PATH . (($i < $c - 1) ? '/folder.png' : '/file.png') . '</icon><text size=small>' . $dir . '</text>');
			}

			self::add_vgap($defs, -34);
			$path .= '/' . $dir;
			$s--;
		}

		self::add_vgap($defs, 150);
	}

	public static function add_group_box(&$defs, &$controls_defs, $title, $viewport_width, $dx = 0, $border_color_index = 0, $border_thickness = 3)
	{
		if (is_null(self::$instance))
			self::init();

		$height = 0;

		if (!isset(self::$instance->dots[$border_color_index]))
			$border_color_index = 0;

		$dx = max(0, $dx);
		$viewport_width = max(300, $viewport_width);
		$border_thickness = min(30, max(1, $border_thickness));
		self::add_smart_label($defs, null, (empty($dx)? '' : '<gap width=' . $dx . '/>') . '<icon width=50 height=' . $border_thickness . '>' . self::$instance->dots[$border_color_index] . '</icon>' . (is_null($title)? '' : '<gap width=12/><text dy=-23>' . $title . '</text><gap width=12/>') . '<icon width=' . ($viewport_width - $dx) . ' height=' . $border_thickness . '>' . self::$instance->dots[$border_color_index] . '</icon>');
		self::add_vgap($defs, -20);
		$height -= 49;

		foreach ($controls_defs as $control_def)
		{
			$defs[] = $control_def;
			$height -= ($control_def['kind'] == GUI_CONTROL_VGAP)? $control_def['specific_def']['vgap'] : 69;
		}

		self::add_smart_label($defs, null, (empty($dx)? '' : '<gap width=' . $dx . '/>') . '<icon width=' . $border_thickness . ' height=' . (0 - $height - $border_thickness) . ' dy=' . ($height + $border_thickness) . '>' . self::$instance->dots[$border_color_index] . '</icon><icon width=' . ($viewport_width - $border_thickness - $border_thickness - $dx) . ' height=' . $border_thickness . ' dy=' . (0 - $border_thickness) . '>' . self::$instance->dots[$border_color_index] . '</icon><icon width=' . $border_thickness . ' height=' . (0 - $height - $border_thickness) . ' dy=' . ($height + $border_thickness) . '>' . self::$instance->dots[$border_color_index] . '</icon>');
		self::add_vgap($defs, -49);
	}

	public static function add_progressbar(&$defs, $dx, $width, $pos_percent, $max_percent = 100)
	{
		if (is_null(self::$instance))
			self::init();

		self::add_smart_label($defs, null, '<gap width=' . ($dx + self::$instance->progressbar_outer_manifest['left_extent']) . '/><icon>' . self::progressbar_outer_left . self::$instance->progressbar_outer_manifest['ext'] . '</icon><icon width=' . ($width - self::$instance->progressbar_outer_manifest['left'] - self::$instance->progressbar_outer_manifest['right']) . ' height=' . self::$instance->progressbar_outer_manifest['height'] . '>' . self::progressbar_outer_center . self::$instance->progressbar_outer_manifest['ext'] . '</icon><icon>' . self::progressbar_outer_right . self::$instance->progressbar_outer_manifest['ext'] . '</icon>');
		$pos_percent  = min($max_percent, max(0, $pos_percent));
		$inner_width = round(($pos_percent * ($width + self::$instance->progressbar_inner_manifest['left_extent'] + self::$instance->progressbar_inner_manifest['right_extent']) / $max_percent));

		if ($inner_width > 0)
		{
			self::add_vgap($defs, -69 - self::$instance->progressbar_inner_manifest['top_extent']);
			self::add_smart_label($defs, null, '<gap width=' . ($dx - self::$instance->progressbar_outer_manifest['left_extent'] - self::$instance->progressbar_inner_manifest['left_extent']) . '/><icon>' . self::progressbar_inner_left . self::$instance->progressbar_inner_manifest['ext'] . '</icon><icon width=' . ($inner_width - self::$instance->progressbar_inner_manifest['left'] - self::$instance->progressbar_inner_manifest['right']) . ' height=' . self::$instance->progressbar_inner_manifest['height'] . '>' . self::progressbar_inner_center . self::$instance->progressbar_inner_manifest['ext'] . '</icon><icon>' . self::progressbar_inner_right . self::$instance->progressbar_inner_manifest['ext'] . '</icon>');
			self::add_vgap($defs, self::$instance->progressbar_inner_manifest['bottom_extent']);
		}
	}

	public static function add_scrollbar(&$defs, $viewport_width, $line_height, $num_of_visible_lines, $num_of_lines, $scroll_position)
	{
		if (is_null(self::$instance))
			self::init();

		if ($num_of_lines <= $num_of_visible_lines)
			return;

		$padding_top = 0 - self::$instance->scrollbar_outer_manifest['top_extent'];
		$padding_bottom = 0 - self::$instance->scrollbar_outer_manifest['bottom_extent'];
		$height = $line_height * $num_of_visible_lines;
		$dx = $viewport_width - ((self::$instance->scrollbar_outer_manifest['width'] > self::$instance->scrollbar_inner_manifest['width'])? self::$instance->scrollbar_outer_manifest['width'] : self::$instance->scrollbar_inner_manifest['width']);
		$gap = $dx - self::$instance->scrollbar_outer_manifest['left_extent'];
		$outer_height = $height - self::$instance->scrollbar_outer_manifest['top'] - self::$instance->scrollbar_outer_manifest['bottom'] + self::$instance->scrollbar_outer_manifest['top_extent'] + self::$instance->scrollbar_outer_manifest['bottom_extent'];

		self::add_vgap($defs, $padding_top - $height);
		self::add_smart_label($defs, null, '<gap width=' . $gap . '/><icon>' . self::scrollbar_outer_top . self::$instance->scrollbar_outer_manifest['ext'] . '</icon>');
		self::add_vgap($defs, self::$instance->scrollbar_outer_manifest['top'] - 69);
		self::add_smart_label($defs, null, '<gap width=' . $gap . '/><icon width=' . self::$instance->scrollbar_outer_manifest['width'] . ' height=' . $outer_height . '>' . self::scrollbar_outer_center . self::$instance->scrollbar_outer_manifest['ext'] . '</icon>');
		self::add_vgap($defs, $outer_height - 69);
		self::add_smart_label($defs, null, '<gap width=' . $gap . '/><icon>' . self::scrollbar_outer_bottom . self::$instance->scrollbar_outer_manifest['ext'] . '</icon>');
		self::add_vgap($defs,  self::$instance->scrollbar_outer_manifest['bottom'] + $padding_bottom - 69);

		$padding_top = self::$instance->scrollbar_inner_manifest['top_extent'];
		$padding_bottom = self::$instance->scrollbar_inner_manifest['bottom_extent'];
		$gap = $dx - self::$instance->scrollbar_inner_manifest['left_extent'];
		$inner_height = $height + $padding_top + $padding_bottom - self::$instance->scrollbar_inner_manifest['top'] - self::$instance->scrollbar_inner_manifest['bottom'];
		$slider_height = max($inner_height - ((($num_of_lines - $num_of_visible_lines) * $line_height) / 2), self::$instance->scrollbar_inner_manifest['top'] + self::$instance->scrollbar_inner_manifest['bottom'] + 10);
		$slider_step = ($inner_height - $slider_height) / ($num_of_lines - $num_of_visible_lines);
		$vgap_inner = round(($slider_step * $scroll_position) - $inner_height);

		self::add_vgap($defs, $vgap_inner + $padding_top - self::$instance->scrollbar_inner_manifest['top'] - self::$instance->scrollbar_inner_manifest['bottom']);
		self::add_smart_label($defs, null, '<gap width=' . $gap . '/><icon>' . self::scrollbar_inner_top . self::$instance->scrollbar_inner_manifest['ext'] . '</icon>');
		self::add_vgap($defs, self::$instance->scrollbar_inner_manifest['top'] - 69);
		self::add_smart_label($defs, null, '<gap width=' . $gap . '/><icon width=' . self::$instance->scrollbar_inner_manifest['width'] . ' height=' . $slider_height . '>' . self::scrollbar_inner_center . self::$instance->scrollbar_inner_manifest['ext'] . '</icon>');
		self::add_vgap($defs, $slider_height - 69);
		self::add_smart_label($defs, null, '<gap width=' . $gap . '/><icon>' . self::scrollbar_inner_bottom . self::$instance->scrollbar_inner_manifest['ext'] . '</icon>');
		self::add_vgap($defs,  abs($vgap_inner) - $slider_height - 69 - $padding_bottom + self::$instance->scrollbar_inner_manifest['bottom']);
	}

	# Вставляет отцентрованную по горизонтали кнопку.
	# Входные данные:
	#	$defs - контролы
	#	$button_defs - контрол кнопки
	# 	$viewport_width - ширина вьюпорта (диалог/папка/др.)
	#
	# Example:
	#	ControlFactory::add_button($button_defs, ...);
	#	ControlFactoryExt::add_button_centered($defs, $button_defs, 1000);
	public static function add_button_centered(&$defs, $button_defs, $viewport_width)
	{
		if (is_null(self::$instance))
			self::init();

		$button = end($button_defs);
		$dx = max(0, $viewport_width - $button[GuiControlDef::specific_def][GuiButtonDef::width] - 120);
		$button[GuiControlDef::title] = self::get_left_margin_str(($dx/28) + round($dx/315));
		$defs[] = $button;
	}

	public static function get_left_margin_str($num_spaces)
	{
		if (is_null(self::$instance))
			self::init();

		if (class_exists("PluginRowsFolderView")) // NEWGUI available
			if (isset(self::$sys_settings_ini['ui_ttf_font']))
				switch(trim(self::$sys_settings_ini['ui_ttf_font']))
				{
					case 'light':
						return str_repeat(' ', intval(round($num_spaces * 1.46376811594203)));
					case 'regular':
						return str_repeat(' ', intval(round($num_spaces * 1.46376811594203)));
					case 'condensed':
						return str_repeat(' ', intval(round($num_spaces * 1.86956521739131)));
					default:
						return str_repeat(' ', intval(round($num_spaces * 1.46376811594203)));
				}

		return str_repeat(' ', intval($num_spaces));
	}
}