root/src/apps/processcontroller/TeamBarMenuItem.cpp
/*
 * Copyright 2000, Georges-Edouard Berenger. All rights reserved.
 * Copyright 2022, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */
#include "TeamBarMenuItem.h"

#include "Colors.h"
#include "ProcessController.h"
#include "ThreadBarMenu.h"
#include "ThreadBarMenuItem.h"
#include "Utilities.h"

#include <Bitmap.h>
#include <ControlLook.h>


TeamBarMenuItem::TeamBarMenuItem(BMenu* menu, BMessage* kill_team, team_id team,
        BBitmap* icon, bool deleteIcon)
        :
        IconMenuItem(icon, menu, true, deleteIcon),
        fTeamID(team)
{
        SetMessage(kill_team);
        Init();
}


void
TeamBarMenuItem::Init()
{
        if (get_team_usage_info(fTeamID, B_TEAM_USAGE_SELF, &fTeamUsageInfo) != B_OK)
                fTeamUsageInfo.kernel_time = fTeamUsageInfo.user_time = 0;

        if (fTeamID == B_SYSTEM_TEAM) {
                thread_info thinfos;
                bigtime_t idle = 0;
                for (unsigned int t = 1; t <= gCPUcount; t++) {
                        if (get_thread_info(t, &thinfos) == B_OK)
                                idle += thinfos.kernel_time + thinfos.user_time;
                }
                fTeamUsageInfo.kernel_time += fTeamUsageInfo.user_time;
                fTeamUsageInfo.user_time = idle;
        }

        fLastTime = system_time();
        fKernel = -1;
        fGrenze1 = -1;
        fGrenze2 = -1;
}


TeamBarMenuItem::~TeamBarMenuItem()
{
}


void
TeamBarMenuItem::DrawContent()
{
        BPoint loc;

        DrawIcon();
        if (fKernel < 0)
                BarUpdate();
        else
                DrawBar(true);

        loc = ContentLocation();
        loc.x += ceilf(be_control_look->DefaultLabelSpacing() * 3.3f);
        Menu()->MovePenTo(loc);
        BMenuItem::DrawContent();
}


void
TeamBarMenuItem::DrawBar(bool force)
{
        const bool selected = IsSelected();
        BRect frame = Frame();
        BMenu* menu = Menu();
        rgb_color highColor = menu->HighColor();

        BFont font;
        menu->GetFont(&font);
        frame = bar_rect(frame, &font);

        if (fKernel < 0)
                return;

        if (fGrenze1 < 0)
                force = true;

        if (force) {
                if (selected)
                        menu->SetHighColor(gFrameColorSelected);
                else
                        menu->SetHighColor(gFrameColor);

                menu->StrokeRect(frame);
        }

        frame.InsetBy(1, 1);
        BRect r = frame;
        float grenze1 = frame.left + (frame.right - frame.left)
                * fKernel / gCPUcount;
        float grenze2 = frame.left + (frame.right - frame.left)
                * (fKernel + fUser) / gCPUcount;

        if (grenze1 > frame.right)
                grenze1 = frame.right;

        if (grenze2 > frame.right)
                grenze2 = frame.right;

        r.right = grenze1;
        if (!force)
                r.left = fGrenze1;

        if (r.left < r.right) {
                if (selected)
                        menu->SetHighColor(gKernelColorSelected);
                else
                        menu->SetHighColor(gKernelColor);

                menu->FillRect(r);
        }

        r.left = grenze1;
        r.right = grenze2;

        if (!force) {
                if (fGrenze2 > r.left && r.left >= fGrenze1)
                        r.left = fGrenze2;

                if (fGrenze1 < r.right && r.right <= fGrenze2)
                        r.right = fGrenze1;
        }

        if (r.left < r.right) {
                if (selected) {
                        menu->SetHighColor(fTeamID == B_SYSTEM_TEAM
                                ? gIdleColorSelected
                                : gUserColorSelected);
                } else {
                        menu->SetHighColor(fTeamID == B_SYSTEM_TEAM
                                ? gIdleColor
                                : gUserColor);
                }

                menu->FillRect(r);
        }

        r.left = grenze2;
        r.right = frame.right;

        if (!force)
                r.right = fGrenze2;

        if (r.left < r.right) {
                if (selected)
                        menu->SetHighColor(gWhiteSelected);
                else
                        menu->SetHighColor(kWhite);

                menu->FillRect(r);
        }

        menu->SetHighColor(highColor);
        fGrenze1 = grenze1;
        fGrenze2 = grenze2;
}


void
TeamBarMenuItem::GetContentSize(float* width, float* height)
{
        IconMenuItem::GetContentSize(width, height);
        if (width != NULL)
                *width += 40 + kBarWidth;
}


void
TeamBarMenuItem::BarUpdate()
{
        team_usage_info usage;
        if (get_team_usage_info(fTeamID, B_TEAM_USAGE_SELF, &usage) == B_OK) {
                bigtime_t now = system_time();
                bigtime_t idle = 0;
                if (fTeamID == B_SYSTEM_TEAM) {
                        thread_info thinfos;
                        for (unsigned int t = 1; t <= gCPUcount; t++) {
                                if (get_thread_info(t, &thinfos) == B_OK)
                                        idle += thinfos.kernel_time + thinfos.user_time;
                        }
                        usage.kernel_time += usage.user_time;
                        usage.user_time = idle;
                        idle -= fTeamUsageInfo.user_time;
                }

                fKernel = double(usage.kernel_time - fTeamUsageInfo.kernel_time - idle)
                        / double(now - fLastTime);

                fUser = double(usage.user_time - fTeamUsageInfo.user_time)
                        / double(now - fLastTime);

                if (fKernel < 0)
                        fKernel = 0;

                fLastTime = now;
                fTeamUsageInfo = usage;
                DrawBar(false);
        } else
                fKernel = -1;
}


void
TeamBarMenuItem::Reset(char* name, team_id team, BBitmap* icon, bool deleteIcon)
{
        IconMenuItem::Reset(icon, deleteIcon);

        SetLabel(name);
        fTeamID = team;
        Init();

        Message()->ReplaceInt32("team", team);
        ((ThreadBarMenu*)Submenu())->Reset(team);
        BarUpdate();
}