;Эффективное программирование в KOLIBRI
;Оптимизированный компонент CheckBox (Исходный вариант от Maxxxx32)
;Оптимизирован вывод строки надписи для CheckBox'a + теперь, при проверке не происходит подсчет кол-ва символов в строке
;Оптимизация команд - отказался от сохранения регистров в стеке.
;17.07.2006 произведена оптимизация, при установке чек бокса в положение включено последующие чек боксы в тот же промежуток времени не проверяются
;<Lrz>  - Теплов Алексей  www.test-kolibri.narod.ru

macro use_check_box
{
ch_text_margin=4     ;расстояние от прямоугольника чек бокса до надписи
ch_size=10	     ;размер квадрата чек бокса
ch_left equ [edi]    ;координата начала рисования по х
ch_top equ [edi+2]   ;координата начала рисования по у
ch_color equ [edi+4] ;цвет внутри checkbox
ch_border_color equ [edi+8] ;цвет рамки checkbox
ch_text_color equ [edi+12]  ;цвет текста
ch_text_ptr equ [edi+16]    ;указатель на начало текстовой строки
ch_text_length equ [edi+20] ;длина надписи (2^64 такой длины может быть текст)
ch_flags equ [edi+24]	    ;флаги
check_box:
.draw:
pusha	;сохраним все регистры
	mov	eax,38		;рисование линии
	mov	bx,ch_left	;положение по х
	;mov     cx,bx           ;сохраним в регистре cx значение bx
	push   bx  ;3 - микрооперации  используя стек можно выиграть в размере, используя регистры - в скорости
	shl	ebx,16		;сдвинем на 16 разрядов в лево (умножим на 65536)
	pop    bx  ;2 - микрооперации  на данный момент сформирована [координата начала по оси x]*65536 + [координата начала по оси x]
	;mov     bx,cx           ;восстановим значение bx
	mov	cx,ch_top	;загрузим в cx значение y
	mov	si,cx		;сохраним значение регистра cx в регистр указатель si
	;push   cx
	shl	ecx,16		; сдвинем на 16 разрядов в лево (умножим на 65536)      mov     cx,si           ;восстановим значение регистра cx
	mov	cx,si		;восстановим значение регистра cx
	;pop    cx      ;[координата начала по оси y]*65536 + [координата начала по оси y]
	;push   cx
	add	cx,ch_size ;[координата начала по оси y]*65536 + [координата конца по оси y]
	mov	edx,ch_border_color ;Цвет линии
	int	0x40   ;рисование вертикальной левой линии квадрата (прямоугольника)
;
	mov	bp,bx	;сохраним регистр bx в регистре указателя базы
	;push   bx   ;втолкнуть в bx [координата начала по оси х]*65536 + [координата начала по оси x]
	add	bx,ch_size	;[координата начала + длина стороны по оси х]
	ror	ebx,16		;[координата начала + дина стороны по оси х]*65536
	add	bx,ch_size	;[координата начала+длина стороны по оси х]*65536 + [координата начала+длина стороны по оси x]
	int	0x40
	mov	bx,bp		;восстановим значение регистра bx
	;pop    bx
	mov	cx,si		;сохраним значение регистра cx в регистр указатель
	;pop    cx
	int	0x40
	add	cx,ch_size	;добавим размер стороны
	mov	si,cx		;сохраним значение регистра cx в регистр указатель si
	;push   cx
	shl	ecx,16
	mov	cx,si
	;pop    cx
	int	0x40		;нарисовали прямоугольник

	mov	eax,13		;закрашиваем его. Функция 13 - нарисовать полосу
	mov	bx,ch_left	;загрузить в bx, положение по х
	add	bx,1		;сдвинем на 1 т.е. прибавим 1 иначе затрется рамка
	shl	ebx,16		;сдвинем на 16 разрядов в лево (умножим на 65536)
	mov	bx,ch_size	;прибавим длину стороны прямоугольника
	sub	bx,1		;вычтем 1 т.к. иначе затрется рамка
	mov	bp,bx		; сохраним регистр bx в регистре указателя базы
	;push   bx
	mov	cx,ch_top	;загрузим координаты по y
	add	cx,1		;сдвинем на 1 т.е. прибавим 1 иначе затрется рамка
	shl	ecx,16		;сдвинем на 16 разрядов в лево (умножим на 65536)
	mov	cx,bp		;восстановим значение регистра cx
	;pop    cx
	mov	edx,ch_color	;загрузим цвет полосы
	int	0x40		;закрасили
	bt	dword ch_flags,1  ;достать значение бита из переменной и поместить в  флаг CF
	jnc	@f		;в если CF=1, то выполним следующую процедуру иначе перейти на нижнюю @@
	call	.draw_ch  ;нарисовать включенный чек бокс
@@:
;----------------------------
;расчет куда будет произведен вывод текста
;----------------------------
	mov	bx,ch_left	;загрузить значение х для чек бокса
	add	bx,(ch_size+ch_text_margin) ;добавить размер стороны и расстояние на котором начнется вывод текста
	shl	ebx,16		;сдвинем на 16 разрядов в лево (умножим на 65536)
	mov	bx,ch_top	;загрузим значение по y
	add	bx,(ch_size-9+2)	;добавим значение длины стороны -9+2
	mov	ecx,ch_text_color	;загрузим цвет надписи

	mov	edx,ch_text_ptr 	;укажем адрес от куда нужно выводить строку
	mov	esi,ch_text_length	;Загрузим длину надписи в esi
	xor	eax,eax 		;внесем в eax значение вывода надписи на канву
	add	eax,4
	int	0x40		;Вывод
popa				;восстановить значения регистров из стека
ret				;выйдем из процедуры

.clear_ch:			;очистка чек бокса
	mov	edx,ch_color	;цвет внутри чек бокса
	jmp	@f		;безусловный прыжок на нижнюю метку @@

.draw_ch:	     ;нарисовать включенный чек бокс
	mov	edx,ch_border_color	;загрузить цвет
@@:
	mov	bx,ch_left	;загрузить координату по х
	add	bx,(ch_size/3)	;добавить (сторона прямоугольника/3)
	shl	ebx,16		;сдвинем на 16 разрядов в лево (умножим на 65536)
	mov	bx,(ch_size/2)	;загрузить (сторона прямоугольника/2)
	mov	bp,bx		;сохраним регистр bx в регистре указателя базы
	;push   bx
	mov	cx,ch_top	;загрузить координату по у
	add	cx,(ch_size/3)	;добавить (сторона прямоугольника/3)
	shl	ecx,16		;сдвинем на 16 разрядов в лево (умножим на 65536)
	mov	cx,bp		;загрузим значения регистра указателя базы в cx
	;pop    cx
	mov	eax,13		;в eax - значения функции для вывода полосы т.е. по сути прямоугольника, который отображает включенный компонент чек бокс
	int	0x40		;вывод
ret				;выйти из процедуры

.mouse: 		;обработка мыши
pusha
	mov	eax,37		;будем что то делать если у нас что - нить нажато
	xor	ebx,ebx 	;обнулить регистр ebx (изменяет флаги)
	add	ebx,2		;внести в регистр значение 2
	int	0x40		;проверка не нажал ли пользователь кнопку мышки
	test	eax,eax    ;проверка если у нас в eax=0, то установим флаг и выйдем
	jnz	@f	   ;перейти на нижнюю метку @@
	btr	dword ch_flags,2  ;извлечение значения заданного бита в флаг cf и изменение его значения на нулевое.
	popa			;если ничего не произошло, то восстановим значения регистров из стека
	ret			;выход
@@:
	bts	dword ch_flags,2   ;проверка флага т.е. перенос в cf значение бита и установка бита в состояние включено
	jc	.mouse_end	   ;если CF=1 то перейти  в конец т.е. это выход
	mov	esi,ch_text_length ;загрузить кол-во символов в текстовой строке
	;Умножение на 6 Быстрое умножение можно воспользоваться любым мз методов, но на старых Процессорах (386,486,P1)быстрее будет с инструкцией Lea
	;lea    esi,[eax*2+eax]
	;shl    eax,1
	imul	esi,6		    ; или можно и так умножить на 6
	add	esi,ch_text_margin   ;добавить 3 - расстояние от чек бокса до надписи

	mov	eax,37		;получим координаты мышки
	xor	ebx,ebx 	;обнулить регистр
	add	ebx,1		;добавить 1
	int	0x40		    ;получить координаты курсора относительно окна

	mov	bx,ch_top	    ;загрузить в bx значение координаты у
	cmp	ax,bx		    ;сравнить с с координатой курсора
	jl	.mouse_end	    ;SF <> OF если меньше
	add	bx,ch_size	    ;добавить размер
	cmp	ax,bx		    ;сравнить
	jg	.mouse_end	    ;ZF = 0 и SF = OF если больше

	shr	eax,16		    ;разделим на 65536 или просто сдвинем биты на 16 значений
	mov	bx,ch_left	    ;произведем аналогичное сравнение
	cmp	ax,bx		     ;сравнить регистры
	jl	.mouse_end	     ;если меньше
	add	bx,ch_size	     ;добавить длину стороны прямоугольника
	add	bx,si		    ;Учесть в значении по х еще и длину надписи к чекбоксу
	cmp	ax,bx		     ;стравнить регистры
	jg	.mouse_end	     ;если больше

	bts	dword ch_flags,1  ;извлечение значения заданного бита в флаг cf и изменение его значения на 1.
	jc	@f		  ;CF=1 то перейти на нижнюю @@

	call	.draw_ch	  ;отобразить включенный чек бокс
	mov	dword [esp+24],1  ;дальнейшая проверка чек боксов бесмыслена, по этому в стек, где располагается ecx поместитм 0
	jmp	.mouse_end	  ;выйти
@@:
	btr	dword ch_flags,1  ;извлечение значения заданного бита в флаг cf и изменение его значения на нулевое.
	call	.clear_ch	  ;выключить чек бокс т.е. на месте закрашенного прямоугольника отобразить цвет фона.
.mouse_end:
popa				  ;восстановить регистры из стека
ret				  ;выйти
}

struc check_box left,top,color,border_color,text_color,text,text_length,flags
{	;структура параметров для чек бокса
.left: dw left	;+0     ;положение по х
.top: dw top	;+2     ;положение по у
.color: dd color ;+4    ;цвет внутри чек бокса
.border_color: dd border_color ;+8      цвет рамки
.text_color: dd text_color     ;+12     цвет текста надписи
.text: dd text	     ;+16               адрес в коде программы где расположен текстр
.text_length: dd text_length ; +20      длина текста
.flags: dd flags+0   ;+24               флаги
}
ch_struc_size=28			;общая структура 28 байт
macro draw_check_boxes start,end	;рисовать чек боксы
{
	mov	edi,start			;Указатель на начало данных чек боксов т.е. на начало данных первого чекбокса
	mov	ecx,((end-start)/ch_struc_size) ;Количество чек боксов
@@:
	call	check_box.draw			;Отобразить чек бокс
	add	edi,ch_struc_size		;Указатель на последующие чек боксы  т.е. +28
	loop	@b	;прыгнуть если в ecx/cx значение не 0 на верхнюю @@
}
macro mouse_check_boxes start,end	;установка чек боксов, в зависимости от события
{
	mov	edi,start		; Указатель на начало данных чек боксов т.е. на начало данных первого чекбокса
	mov	ecx,((end-start)/ch_struc_size) ;Количество чек боксов
@@:
	call	check_box.mouse 	;проверка мышки и обработка событий
	add	edi,ch_struc_size	;Указатель на последующие чек боксы
	loop	@b	;прыгнуть если в ecx/cx значение не 0 на верхнюю @@
}
macro check_boxes_set_sys_color start,end,color_table
{
	mov	edi,start
	mov	ecx,((end-start)/ch_struc_size)
	mov	esi,color_table
@@:
	push	dword [esi+32]
	pop	dword ch_text_color
	push	dword [esi+36]
	pop	dword ch_border_color
	add	edi,ch_struc_size
	loop	@b
}