* Copyright 2005-2009, Haiku Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "WorkspacesView.h"
#include "AppServer.h"
#include "Decorator.h"
#include "Desktop.h"
#include "DrawingEngine.h"
#include "DrawState.h"
#include "InterfaceDefs.h"
#include "ServerApp.h"
#include "Window.h"
#include "Workspace.h"
#include <WindowPrivate.h>
WorkspacesView::WorkspacesView(BRect frame, BPoint scrollingOffset,
const char* name, int32 token, uint32 resizeMode, uint32 flags)
:
View(frame, scrollingOffset, name, token, resizeMode, flags),
fSelectedWindow(NULL),
fSelectedWorkspace(-1),
fHasMoved(false)
{
fDrawState->SetLowColor((rgb_color){ 255, 255, 255, 255 });
fDrawState->SetHighColor((rgb_color){ 0, 0, 0, 255 });
}
WorkspacesView::~WorkspacesView()
{
}
void
WorkspacesView::AttachedToWindow(::Window* window)
{
View::AttachedToWindow(window);
window->AddWorkspacesView();
window->Desktop()->AddWorkspacesView(this);
}
void
WorkspacesView::DetachedFromWindow()
{
fWindow->Desktop()->RemoveWorkspacesView(this);
fWindow->RemoveWorkspacesView();
View::DetachedFromWindow();
}
void
WorkspacesView::_GetGrid(int32& columns, int32& rows)
{
DesktopSettings settings(Window()->Desktop());
columns = settings.WorkspacesColumns();
rows = settings.WorkspacesRows();
}
*/
BRect
WorkspacesView::_ScreenFrame(int32 i)
{
return Window()->Desktop()->WorkspaceFrame(i);
}
workspaces view.
*/
BRect
WorkspacesView::_WorkspaceAt(int32 i)
{
int32 columns, rows;
_GetGrid(columns, rows);
BRect frame = Bounds();
LocalToScreenTransform().Apply(&frame);
int32 width = frame.IntegerWidth() / columns;
int32 height = frame.IntegerHeight() / rows;
int32 column = i % columns;
int32 row = i / columns;
BRect rect(column * width, row * height, (column + 1) * width,
(row + 1) * height);
rect.OffsetBy(frame.LeftTop());
if (column == columns - 1)
rect.right = frame.right;
if (row == rows - 1)
rect.bottom = frame.bottom;
return rect;
}
under \a where.
If, for some reason, there is no workspace located under \where,
an empty rectangle is returned, and \a index is set to -1.
*/
BRect
WorkspacesView::_WorkspaceAt(BPoint where, int32& index)
{
int32 columns, rows;
_GetGrid(columns, rows);
for (index = columns * rows; index-- > 0;) {
BRect workspaceFrame = _WorkspaceAt(index);
if (workspaceFrame.Contains(where))
return workspaceFrame;
}
return BRect();
}
BRect
WorkspacesView::_WindowFrame(const BRect& workspaceFrame,
const BRect& screenFrame, const BRect& windowFrame,
BPoint windowPosition)
{
BRect frame = windowFrame;
frame.OffsetTo(windowPosition);
float factor = workspaceFrame.Width() / screenFrame.Width();
frame.left = frame.left * factor;
frame.right = frame.right * factor;
factor = workspaceFrame.Height() / screenFrame.Height();
frame.top = frame.top * factor;
frame.bottom = frame.bottom * factor;
frame.OffsetTo(rintf(frame.left + workspaceFrame.left),
rintf(frame.top + workspaceFrame.top));
frame.right = rintf(frame.right);
frame.bottom = rintf(frame.bottom);
return frame;
}
void
WorkspacesView::_DrawWindow(DrawingEngine* drawingEngine,
const BRect& workspaceFrame, const BRect& screenFrame, ::Window* window,
BPoint windowPosition, BRegion& backgroundRegion, bool workspaceActive)
{
if (window->Feel() == kDesktopWindowFeel || window->IsHidden())
return;
BPoint offset = window->Frame().LeftTop() - windowPosition;
BRect frame = _WindowFrame(workspaceFrame, screenFrame, window->Frame(),
windowPosition);
Decorator *decorator = window->Decorator();
BRect tabFrame(0, 0, 0, 0);
if (decorator != NULL)
tabFrame = decorator->TitleBarRect();
tabFrame = _WindowFrame(workspaceFrame, screenFrame,
tabFrame, tabFrame.LeftTop() - offset);
if (!workspaceFrame.Intersects(frame)
&& !workspaceFrame.Intersects(tabFrame))
return;
rgb_color activeTabColor = (rgb_color){ 255, 203, 0, 255 };
rgb_color inactiveTabColor = (rgb_color){ 232, 232, 232, 255 };
rgb_color navColor = (rgb_color){ 0, 0, 229, 255 };
rgb_color activeFrameColor = (rgb_color){ 180, 180, 180, 255 };
rgb_color inactiveFrameColor = (rgb_color){ 180, 180, 180, 255 };
if (decorator != NULL) {
activeTabColor = decorator->UIColor(B_WINDOW_TAB_COLOR);
inactiveTabColor = decorator->UIColor(B_WINDOW_INACTIVE_TAB_COLOR);
navColor = decorator->UIColor(B_NAVIGATION_BASE_COLOR);
activeFrameColor = decorator->UIColor(B_WINDOW_BORDER_COLOR);
inactiveFrameColor = decorator->UIColor(B_WINDOW_INACTIVE_BORDER_COLOR);
}
rgb_color white = (rgb_color){ 255, 255, 255, 255 };
rgb_color tabColor = inactiveTabColor;
rgb_color frameColor = inactiveFrameColor;
if (window->IsFocus()) {
tabColor = activeTabColor;
frameColor = activeFrameColor;
}
if (!workspaceActive) {
_DarkenColor(tabColor);
_DarkenColor(frameColor);
_DarkenColor(white);
}
if (tabFrame.Height() > 0 && tabFrame.Width() > 0) {
float width = tabFrame.Width();
if (tabFrame.left < frame.left) {
tabFrame.left = frame.left;
tabFrame.right = tabFrame.left + width;
}
if (tabFrame.right > frame.right) {
tabFrame.right = frame.right;
tabFrame.left = tabFrame.right - width;
}
if (tabFrame.bottom >= tabFrame.top) {
float tabHeight = tabFrame.Height();
tabFrame.bottom = frame.top - 1;
tabFrame.top = tabFrame.bottom - tabHeight;
}
tabFrame = tabFrame & workspaceFrame;
if (decorator != NULL && tabFrame.IsValid()) {
drawingEngine->FillRect(tabFrame, tabColor);
backgroundRegion.Exclude(tabFrame);
}
}
drawingEngine->StrokeRect(frame, frameColor);
BRect fillFrame = frame.InsetByCopy(1, 1) & workspaceFrame;
frame = frame & workspaceFrame;
if (!frame.IsValid())
return;
drawingEngine->FillRect(fillFrame, white);
BString title(window->Title());
const ServerFont& font = fDrawState->Font();
font.TruncateString(&title, B_TRUNCATE_END, fillFrame.Width() - 4);
font_height fontHeight;
font.GetHeight(fontHeight);
float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
if (title.Length() > 0 && height < frame.Height() - 2) {
rgb_color textColor = tint_color(white, B_DARKEN_4_TINT);
drawingEngine->SetHighColor(textColor);
drawingEngine->SetLowColor(white);
float width = font.StringWidth(title.String(), title.Length());
BPoint textOffset;
textOffset.x = rintf(frame.left + (frame.Width() - width) / 2);
textOffset.y = rintf(frame.top + (frame.Height() - height) / 2
+ fontHeight.ascent);
drawingEngine->DrawString(title.String(), title.Length(), textOffset);
}
backgroundRegion.Exclude(frame);
}
void
WorkspacesView::_DrawWorkspace(DrawingEngine* drawingEngine,
BRegion& redraw, int32 index)
{
BRect rect = _WorkspaceAt(index);
Workspace workspace(*Window()->Desktop(), index, true);
bool workspaceActive = workspace.IsCurrent();
if (workspaceActive) {
rgb_color black = (rgb_color){ 0, 0, 0, 255 };
drawingEngine->StrokeRect(rect, black);
} else if (index == fSelectedWorkspace) {
rgb_color gray = (rgb_color){ 80, 80, 80, 255 };
drawingEngine->StrokeRect(rect, gray);
}
rect.InsetBy(1, 1);
rgb_color color = workspace.Color();
if (!workspaceActive)
_DarkenColor(color);
BRegion backgroundRegion = redraw;
BRect screenFrame = _ScreenFrame(index);
BRegion workspaceRegion(rect);
backgroundRegion.IntersectWith(&workspaceRegion);
drawingEngine->ConstrainClippingRegion(&backgroundRegion);
ServerFont font = fDrawState->Font();
font.SetSize(fWindow->ServerWindow()->App()->PlainFont().Size());
float reducedSize = ceilf(max_c(8.0f,
min_c(Frame().Height(), Frame().Width()) / 15));
if (font.Size() > reducedSize)
font.SetSize(reducedSize);
fDrawState->SetFont(font);
drawingEngine->SetFont(font);
::Window* window;
BPoint leftTop;
while (workspace.GetPreviousWindow(window, leftTop) == B_OK) {
_DrawWindow(drawingEngine, rect, screenFrame, window,
leftTop, backgroundRegion, workspaceActive);
}
drawingEngine->FillRect(rect, color);
drawingEngine->ConstrainClippingRegion(&redraw);
}
void
WorkspacesView::_DarkenColor(rgb_color& color) const
{
color = tint_color(color, B_DARKEN_2_TINT);
}
void
WorkspacesView::_Invalidate() const
{
BRect frame = Bounds();
LocalToScreenTransform().Apply(&frame);
BRegion region(frame), expose;
Window()->MarkContentDirty(region, expose);
}
void
WorkspacesView::Draw(DrawingEngine* drawingEngine, const BRegion* effectiveClipping,
const BRegion* windowContentClipping, bool deep)
{
BRegion redraw(ScreenAndUserClipping(windowContentClipping));
redraw.IntersectWith(effectiveClipping);
int32 columns, rows;
_GetGrid(columns, rows);
BRect activeRect = _WorkspaceAt(Window()->Desktop()->CurrentWorkspace());
BRegion gridRegion(redraw);
gridRegion.Exclude(activeRect);
drawingEngine->ConstrainClippingRegion(&gridRegion);
BRect frame = Bounds();
LocalToScreenTransform().Apply(&frame);
drawingEngine->StrokeLine(BPoint(frame.left, frame.top),
BPoint(frame.right, frame.top), ViewColor());
for (int32 row = 0; row < rows; row++) {
BRect rect = _WorkspaceAt(row * columns);
drawingEngine->StrokeLine(BPoint(frame.left, rect.bottom),
BPoint(frame.right, rect.bottom), ViewColor());
}
drawingEngine->StrokeLine(BPoint(frame.left, frame.top),
BPoint(frame.left, frame.bottom), ViewColor());
for (int32 column = 0; column < columns; column++) {
BRect rect = _WorkspaceAt(column);
drawingEngine->StrokeLine(BPoint(rect.right, frame.top),
BPoint(rect.right, frame.bottom), ViewColor());
}
drawingEngine->ConstrainClippingRegion(&redraw);
for (int32 i = rows * columns; i-- > 0;) {
_DrawWorkspace(drawingEngine, redraw, i);
}
fWindow->ServerWindow()->ResyncDrawState();
}
void
WorkspacesView::MouseDown(BMessage* message, BPoint where)
{
fSelectedWorkspace = -1;
fSelectedWindow = NULL;
fHasMoved = false;
int32 buttons;
if (message->FindInt32("buttons", &buttons) != B_OK
|| (buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
return;
int32 index;
BRect workspaceFrame = _WorkspaceAt(where, index);
if (index < 0)
return;
Workspace workspace(*Window()->Desktop(), index);
workspaceFrame.InsetBy(1, 1);
BRect screenFrame = _ScreenFrame(index);
::Window* window;
BRect windowFrame;
BPoint leftTop;
while (workspace.GetPreviousWindow(window, leftTop) == B_OK) {
if (window->IsMinimized() || window->IsHidden())
continue;
BRect frame = _WindowFrame(workspaceFrame, screenFrame, window->Frame(),
leftTop);
if (frame.Contains(where) && window->Feel() != kDesktopWindowFeel
&& window->Feel() != kWindowScreenFeel) {
fSelectedWindow = window;
windowFrame = frame;
break;
}
}
int32 modifiers;
if (fSelectedWindow != NULL
&& message->FindInt32("modifiers", &modifiers) == B_OK) {
if ((modifiers & B_CONTROL_KEY) != 0) {
if ((modifiers & B_SHIFT_KEY) != 0)
fSelectedWindow->ServerWindow()->NotifyMinimize(true);
else
Window()->Desktop()->ActivateWindow(fSelectedWindow);
fSelectedWindow = NULL;
} else if ((modifiers & B_OPTION_KEY) != 0) {
Window()->Desktop()->SendWindowBehind(fSelectedWindow);
fSelectedWindow = NULL;
}
}
if ((fSelectedWindow != NULL
&& (fSelectedWindow->Flags() & B_NOT_MOVABLE) != 0)
|| fSelectedWindow == Window()) {
fSelectedWindow = NULL;
}
fLeftTopOffset = where - windowFrame.LeftTop();
fSelectedWorkspace = index;
fClickPoint = where;
if (index >= 0)
_Invalidate();
}
void
WorkspacesView::MouseUp(BMessage* message, BPoint where)
{
if (!fHasMoved && fSelectedWorkspace >= 0) {
int32 index;
_WorkspaceAt(where, index);
if (index >= 0)
Window()->Desktop()->SetWorkspaceAsync(index);
}
if (fSelectedWindow != NULL) {
_Invalidate();
}
fSelectedWindow = NULL;
fSelectedWorkspace = -1;
}
void
WorkspacesView::MouseMoved(BMessage* message, BPoint where)
{
if (fSelectedWindow == NULL && fSelectedWorkspace < 0)
return;
int32 buttons;
if (message->FindInt32("buttons", &buttons) != B_OK
|| (buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
return;
if (!fHasMoved) {
Window()->Desktop()->SetMouseEventWindow(Window());
}
int32 index;
BRect workspaceFrame = _WorkspaceAt(where, index);
if (fSelectedWindow == NULL) {
if (fSelectedWorkspace >= 0 && fSelectedWorkspace != index) {
fSelectedWorkspace = index;
_Invalidate();
}
return;
}
workspaceFrame.InsetBy(1, 1);
if (index != fSelectedWorkspace) {
if (fSelectedWindow->IsNormal() && !fSelectedWindow->InWorkspace(index)) {
uint32 newWorkspaces = (fSelectedWindow->Workspaces()
& ~(1UL << fSelectedWorkspace)) | (1UL << index);
Window()->Desktop()->SetWindowWorkspaces(fSelectedWindow,
newWorkspaces);
}
fSelectedWorkspace = index;
}
BRect screenFrame = _ScreenFrame(index);
float left = rintf((where.x - workspaceFrame.left - fLeftTopOffset.x)
* screenFrame.Width() / workspaceFrame.Width());
float top = rintf((where.y - workspaceFrame.top - fLeftTopOffset.y)
* screenFrame.Height() / workspaceFrame.Height());
BPoint leftTop;
if (fSelectedWorkspace == Window()->Desktop()->CurrentWorkspace())
leftTop = fSelectedWindow->Frame().LeftTop();
else {
if (fSelectedWindow->Anchor(fSelectedWorkspace).position
== kInvalidWindowPosition) {
fSelectedWindow->Anchor(fSelectedWorkspace).position
= fSelectedWindow->Frame().LeftTop();
}
leftTop = fSelectedWindow->Anchor(fSelectedWorkspace).position;
}
float diff = max_c(fabs(fClickPoint.x - where.x),
fabs(fClickPoint.y - where.y));
if (!fHasMoved && diff > 2)
fHasMoved = true;
if (fHasMoved) {
Window()->Desktop()->MoveWindowBy(fSelectedWindow, left - leftTop.x,
top - leftTop.y, fSelectedWorkspace);
}
}
void
WorkspacesView::WindowChanged(::Window* window)
{
_Invalidate();
}
void
WorkspacesView::WindowRemoved(::Window* window)
{
if (fSelectedWindow == window)
fSelectedWindow = NULL;
}