{
    RunningListWindow используется для создания окна, в котором пользователь
    смотрит список запущенных приложений и взаимодействует с ним.
    Этот исходный текст является частью Малик Эмулятора.

    Следующие файлы используются этим исходным текстом:
        runninglistwindow.lfm
    На них так же распространяются те же права, как и на этот исходный текст.

    Copyright © 2016–2017, 2019–2022 Малик Разработчик

    Малик Эмулятор – свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Малик Эмулятор распространяется в надежде, что он может быть полезен,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit RunningListWindow;

{$MODE DELPHI}

interface

uses
    Classes,
    SysUtils,
    Forms,
    Controls,
    Graphics,
    LCLType,
    Lang,
    Images,
    Manifests,
    EmulConstants,
    EmulProgrammes,
    EmulThemes,
    EmulatorInterfaces;

{%region public }
type
    TRunningListForm = class(TForm, GraphicListener)
        procedure formHide(sender: TObject);
        procedure formPaint(sender: TObject);
        procedure formKeyDown(sender: TObject; var key: Word; shift: TShiftState);
        procedure formKeyUp(sender: TObject; var key: Word; shift: TShiftState);
        procedure formMouseDown(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: Integer);
        procedure formMouseUp(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: Integer);
    private
        owner: MainWindowInterface;
        manifest: ProgrammeManifest;
        icon: TIcon;
        selectedIndex: int;
        image: TBitmap;
        imageWidth: int;
        imageHeight: int;
    public
        constructor create(theOwner: TComponent); override;
        destructor destroy; override;
        procedure afterConstruction(); override;
        procedure beforeDestruction(); override;
        procedure putPixel(x, y, argb: int);
        procedure updateList();
        function getClass(): _Class;
        function asObject(): TObject;
    end;
{%endregion}

implementation

{$R *.LFM}

{%region TRunningListForm }
    constructor TRunningListForm.create(theOwner: TComponent);
    var
        width: int;
        height: int;
    begin
        inherited create(theOwner);
        theOwner.getInterface(MAIN_WINDOW_INTERFACE_GUID, owner);
        width := self.clientWidth;
        height := self.clientHeight;
        manifest := ProgrammeManifest.create();
        icon := TIcon.create();
        image := TBitmap.create();
        imageWidth := width;
        imageHeight := height;
        image.setSize(width, height);
    end;

    destructor TRunningListForm.destroy;
    begin
        icon.free();
        image.free();
        manifest.free();
        inherited destroy;
    end;

    procedure TRunningListForm.formHide(sender: TObject);
    begin
        owner.notifyTerminated(nil);
    end;

    procedure TRunningListForm.formPaint(sender: TObject);
    var
        i: int;
        w: int;
        h: int;
        count: int;
        left: int;
        top: int;
        width: int;
        height: int;
        canvas: TCanvas;
        clip: TRect;
        info: ProgrammeInfo;
        m: ProgrammeManifest;
        n: TIcon;
        s: AnsiString;
    begin
        width := imageWidth;
        height := imageHeight;
        canvas := image.canvas;
        canvas.brush.color := clWhite;
        canvas.clipRect := bounds(0, 0, width, height);
        canvas.fillRect(bounds(0, 0, width, height));
        left := 0;
        top := 0;
        height := 52;
        m := self.manifest;
        n := self.icon;
        count := owner.getEmulatorsCount();
        for i := 0 to count - 1 do begin
            clip := bounds(left, top, width, height);
            canvas.clipRect := clip;
            if i = selectedIndex then begin
                getTheme(0).drawElement(self, $000001, left, top, width, height);
            end;
            info := ProgrammeInfo.create(owner.getEmulator(i).getProgrammeDirectory());
            try
                info.loadManifest(m);
                info.loadIcon(n);
                findBestIconSize(n, 48);
                clip := bounds(left + 2, top + 2, 48, 48);
                canvas.clipRect := clip;
                w := n.width;
                h := n.height;
                if (w > 48) or (h > 48) then begin
                    if w >= h then begin
                        h := int(round(48.0 * toReal(h) / toReal(w)));
                        w := 48;
                    end else begin
                        w := int(round(48.0 * toReal(w) / toReal(h)));
                        h := 48;
                    end;
                    canvas.stretchDraw(bounds(clip.left + ((48 - w) div 2), clip.top + ((48 - h) div 2), w, h), n);
                end else begin
                    canvas.draw(clip.left + ((48 - w) div 2), clip.top + ((48 - h) div 2), n);
                end;
                clip := rect(left + 52, top + 2, left + width - 2, top + height - 2);
                canvas.clipRect := clip;
                s := trim(m.getValue(MANIFEST_PROPERTY_PROGRAMME_NAME));
                canvas.brush.style := bsClear;
                canvas.font.color := clBlack;
                canvas.textOut(left + 52, top + 18, s);
            finally
                info.free();
            end;
            inc(top, height);
        end;
        self.canvas.draw(0, 0, image);
    end;

    procedure TRunningListForm.formKeyDown(sender: TObject; var key: Word; shift: TShiftState);
    var
        count: int;
    begin
        if (key = VK_M) and (shift * [ssShift, ssCtrl, ssAlt] = [ssAlt]) then begin
            { Alt+M – показать главное окно }
            owner.showMainWindow();
            key := 0;
            exit;
        end;
        if (key = VK_UP) and (shift * [ssShift, ssCtrl, ssAlt] = []) then begin
            { ↑ – переместить вверх }
            count := owner.getEmulatorsCount();
            if count > 0 then begin
                selectedIndex := (selectedIndex + count - 1) mod count;
                repaint();
            end;
            key := 0;
            exit;
        end;
        if (key = VK_DOWN) and (shift * [ssShift, ssCtrl, ssAlt] = []) then begin
            { ↓ – переместить вниз }
            count := owner.getEmulatorsCount();
            if count > 0 then begin
                selectedIndex := (selectedIndex + 1) mod count;
                repaint();
            end;
            key := 0;
            exit;
        end;
    end;

    procedure TRunningListForm.formKeyUp(sender: TObject; var key: Word; shift: TShiftState);
    var
        index: int;
    begin
        if (key = VK_RETURN) and (shift * [ssShift, ssCtrl, ssAlt] = []) then begin
            { Enter – переключиться на выбранную программу }
            index := selectedIndex;
            if index < owner.getEmulatorsCount() then begin
                owner.getEmulator(index).showEmulationWindow();
            end;
            key := 0;
            exit;
        end;
    end;

    procedure TRunningListForm.formMouseDown(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: Integer);
    var
        count: int;
        index: int;
    begin
        if button = TMouseButton.mbLeft then begin
            index := y div 52;
            count := owner.getEmulatorsCount();
            if (index >= 0) and (index < count) then begin
                selectedIndex := index;
                formPaint(self);
            end;
        end;
    end;

    procedure TRunningListForm.formMouseUp(sender: TObject; button: TMouseButton; shift: TShiftState; x, y: Integer);
    var
        index: int;
        left: int;
        top: int;
        right: int;
        bottom: int;
    begin
        index := selectedIndex;
        if index < owner.getEmulatorsCount() then begin
            left := 0;
            top := selectedIndex * 52;
            right := imageWidth;
            bottom := top + 52;
            if (button = TMouseButton.mbLeft) and (x >= left) and (x < right) and (y >= top) and (y < bottom) then begin
                owner.getEmulator(index).showEmulationWindow();
            end;
        end;
    end;

    procedure TRunningListForm.afterConstruction();
    var
        x: int;
        y: int;
        c: AnsiString_Array1d;
    begin
        inherited afterConstruction();
        x := 0;
        y := 0;
        c := getComponents(owner.getSetting(SECTION_WINDOWS, KEY_RUNNING_PROGRAMMES, toDecString(x) + ',' + toDecString(y)));
        if length(c) >= 2 then begin
            left := parseDecInt(c[0], x);
            top := parseDecInt(c[1], y);
        end else begin
            left := x;
            top := y;
        end;
    end;

    procedure TRunningListForm.beforeDestruction();
    begin
        owner.setSetting(SECTION_WINDOWS, KEY_RUNNING_PROGRAMMES, toDecString(left) + ',' + toDecString(top));
        inherited beforeDestruction();
    end;

    procedure TRunningListForm.putPixel(x, y, argb: int);
    var
        canvas: TCanvas;
    begin
        canvas := image.canvas;
        canvas.pixels[x, y] := byteSwap(computePixel(byteSwap(canvas.pixels[x, y]) shr 8, argb, false, true)) shr 8;
    end;

    procedure TRunningListForm.updateList();
    var
        count: int;
    begin
        count := owner.getEmulatorsCount();
        if count > 0 then begin
            dec(count);
            if selectedIndex > count then begin
                selectedIndex := count;
            end;
        end;
        formPaint(self);
    end;

    function TRunningListForm.getClass(): _Class;
    begin
        result := ClassData.create(classType());
    end;

    function TRunningListForm.asObject(): TObject;
    begin
        result := self;
    end;
{%endregion}

end.

