usb_ecm: Use the current configuration instead of a fixed one.
[haiku.git] / src / servers / app / WorkspacesView.cpp
blob2e4aabac75d35843f74b77a089af671b00db2d85
1 /*
2 * Copyright 2005-2009, Haiku Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Stephan Aßmus <superstippi@gmx.de>
8 */
11 #include "WorkspacesView.h"
13 #include "AppServer.h"
14 #include "Decorator.h"
15 #include "Desktop.h"
16 #include "DrawingEngine.h"
17 #include "DrawState.h"
18 #include "InterfaceDefs.h"
19 #include "ServerApp.h"
20 #include "Window.h"
21 #include "Workspace.h"
23 #include <WindowPrivate.h>
26 WorkspacesView::WorkspacesView(BRect frame, BPoint scrollingOffset,
27 const char* name, int32 token, uint32 resizeMode, uint32 flags)
29 View(frame, scrollingOffset, name, token, resizeMode, flags),
30 fSelectedWindow(NULL),
31 fSelectedWorkspace(-1),
32 fHasMoved(false)
34 fDrawState->SetLowColor((rgb_color){ 255, 255, 255, 255 });
35 fDrawState->SetHighColor((rgb_color){ 0, 0, 0, 255 });
39 WorkspacesView::~WorkspacesView()
44 void
45 WorkspacesView::AttachedToWindow(::Window* window)
47 View::AttachedToWindow(window);
49 window->AddWorkspacesView();
50 window->Desktop()->AddWorkspacesView(this);
54 void
55 WorkspacesView::DetachedFromWindow()
57 fWindow->Desktop()->RemoveWorkspacesView(this);
58 fWindow->RemoveWorkspacesView();
60 View::DetachedFromWindow();
64 void
65 WorkspacesView::_GetGrid(int32& columns, int32& rows)
67 DesktopSettings settings(Window()->Desktop());
69 columns = settings.WorkspacesColumns();
70 rows = settings.WorkspacesRows();
74 /*! \brief Returns the frame of the screen for the specified workspace.
76 BRect
77 WorkspacesView::_ScreenFrame(int32 i)
79 return Window()->Desktop()->WorkspaceFrame(i);
83 /*! \brief Returns the frame of the specified workspace within the
84 workspaces view.
86 BRect
87 WorkspacesView::_WorkspaceAt(int32 i)
89 int32 columns, rows;
90 _GetGrid(columns, rows);
92 BRect frame = Bounds();
93 LocalToScreenTransform().Apply(&frame);
95 int32 width = frame.IntegerWidth() / columns;
96 int32 height = frame.IntegerHeight() / rows;
98 int32 column = i % columns;
99 int32 row = i / columns;
101 BRect rect(column * width, row * height, (column + 1) * width,
102 (row + 1) * height);
104 rect.OffsetBy(frame.LeftTop());
106 // make sure there is no gap anywhere
107 if (column == columns - 1)
108 rect.right = frame.right;
109 if (row == rows - 1)
110 rect.bottom = frame.bottom;
112 return rect;
116 /*! \brief Returns the workspace frame and index of the workspace
117 under \a where.
119 If, for some reason, there is no workspace located under \where,
120 an empty rectangle is returned, and \a index is set to -1.
122 BRect
123 WorkspacesView::_WorkspaceAt(BPoint where, int32& index)
125 int32 columns, rows;
126 _GetGrid(columns, rows);
128 for (index = columns * rows; index-- > 0;) {
129 BRect workspaceFrame = _WorkspaceAt(index);
131 if (workspaceFrame.Contains(where))
132 return workspaceFrame;
135 return BRect();
139 BRect
140 WorkspacesView::_WindowFrame(const BRect& workspaceFrame,
141 const BRect& screenFrame, const BRect& windowFrame,
142 BPoint windowPosition)
144 BRect frame = windowFrame;
145 frame.OffsetTo(windowPosition);
147 // scale down the rect
148 float factor = workspaceFrame.Width() / screenFrame.Width();
149 frame.left = frame.left * factor;
150 frame.right = frame.right * factor;
152 factor = workspaceFrame.Height() / screenFrame.Height();
153 frame.top = frame.top * factor;
154 frame.bottom = frame.bottom * factor;
156 // offset by the workspace fame position
157 // and snap to integer coordinates without distorting the size too much
158 frame.OffsetTo(rintf(frame.left + workspaceFrame.left),
159 rintf(frame.top + workspaceFrame.top));
160 frame.right = rintf(frame.right);
161 frame.bottom = rintf(frame.bottom);
163 return frame;
167 void
168 WorkspacesView::_DrawWindow(DrawingEngine* drawingEngine,
169 const BRect& workspaceFrame, const BRect& screenFrame, ::Window* window,
170 BPoint windowPosition, BRegion& backgroundRegion, bool workspaceActive)
172 if (window->Feel() == kDesktopWindowFeel || window->IsHidden())
173 return;
175 BPoint offset = window->Frame().LeftTop() - windowPosition;
176 BRect frame = _WindowFrame(workspaceFrame, screenFrame, window->Frame(),
177 windowPosition);
178 Decorator *decorator = window->Decorator();
179 BRect tabFrame(0, 0, 0, 0);
180 if (decorator != NULL)
181 tabFrame = decorator->TitleBarRect();
183 tabFrame = _WindowFrame(workspaceFrame, screenFrame,
184 tabFrame, tabFrame.LeftTop() - offset);
185 if (!workspaceFrame.Intersects(frame)
186 && !workspaceFrame.Intersects(tabFrame))
187 return;
189 rgb_color activeTabColor = (rgb_color){ 255, 203, 0, 255 };
190 rgb_color inactiveTabColor = (rgb_color){ 232, 232, 232, 255 };
191 rgb_color navColor = (rgb_color){ 0, 0, 229, 255 };
192 rgb_color activeFrameColor = (rgb_color){ 180, 180, 180, 255 };
193 rgb_color inactiveFrameColor = (rgb_color){ 180, 180, 180, 255 };
194 if (decorator != NULL) {
195 activeTabColor = decorator->UIColor(B_WINDOW_TAB_COLOR);
196 inactiveTabColor = decorator->UIColor(B_WINDOW_INACTIVE_TAB_COLOR);
197 navColor = decorator->UIColor(B_NAVIGATION_BASE_COLOR);
198 activeFrameColor = decorator->UIColor(B_WINDOW_BORDER_COLOR);
199 inactiveFrameColor = decorator->UIColor(B_WINDOW_INACTIVE_BORDER_COLOR);
202 rgb_color white = (rgb_color){ 255, 255, 255, 255 };
204 rgb_color tabColor = inactiveTabColor;
205 rgb_color frameColor = inactiveFrameColor;
206 if (window->IsFocus()) {
207 tabColor = activeTabColor;
208 frameColor = activeFrameColor;
211 if (!workspaceActive) {
212 _DarkenColor(tabColor);
213 _DarkenColor(frameColor);
214 _DarkenColor(white);
217 if (tabFrame.Height() > 0 && tabFrame.Width() > 0) {
218 float width = tabFrame.Width();
219 if (tabFrame.left < frame.left) {
220 // Shift the tab right
221 tabFrame.left = frame.left;
222 tabFrame.right = tabFrame.left + width;
224 if (tabFrame.right > frame.right) {
225 // Shift the tab left
226 tabFrame.right = frame.right;
227 tabFrame.left = tabFrame.right - width;
230 if (tabFrame.bottom >= tabFrame.top) {
231 // Shift the tab up
232 float tabHeight = tabFrame.Height();
233 tabFrame.bottom = frame.top - 1;
234 tabFrame.top = tabFrame.bottom - tabHeight;
237 tabFrame = tabFrame & workspaceFrame;
239 if (decorator != NULL && tabFrame.IsValid()) {
240 drawingEngine->FillRect(tabFrame, tabColor);
241 backgroundRegion.Exclude(tabFrame);
245 drawingEngine->StrokeRect(frame, frameColor);
247 BRect fillFrame = frame.InsetByCopy(1, 1) & workspaceFrame;
248 frame = frame & workspaceFrame;
249 if (!frame.IsValid())
250 return;
252 // fill the window itself
253 drawingEngine->FillRect(fillFrame, white);
255 // draw title
257 // TODO: the mini-window functionality should probably be moved into the
258 // Window class, so that it has only to be recalculated on demand. With
259 // double buffered windows, this would also open up the door to have a
260 // more detailed preview.
261 BString title(window->Title());
263 const ServerFont& font = fDrawState->Font();
265 font.TruncateString(&title, B_TRUNCATE_END, fillFrame.Width() - 4);
266 font_height fontHeight;
267 font.GetHeight(fontHeight);
268 float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
269 if (title.Length() > 0 && height < frame.Height() - 2) {
270 rgb_color textColor = tint_color(white, B_DARKEN_4_TINT);
271 drawingEngine->SetHighColor(textColor);
272 drawingEngine->SetLowColor(white);
274 float width = font.StringWidth(title.String(), title.Length());
276 BPoint textOffset;
277 textOffset.x = rintf(frame.left + (frame.Width() - width) / 2);
278 textOffset.y = rintf(frame.top + (frame.Height() - height) / 2
279 + fontHeight.ascent);
280 drawingEngine->DrawString(title.String(), title.Length(), textOffset);
283 // prevent the next window down from drawing over this window
284 backgroundRegion.Exclude(frame);
288 void
289 WorkspacesView::_DrawWorkspace(DrawingEngine* drawingEngine,
290 BRegion& redraw, int32 index)
292 BRect rect = _WorkspaceAt(index);
294 Workspace workspace(*Window()->Desktop(), index, true);
295 bool workspaceActive = workspace.IsCurrent();
296 if (workspaceActive) {
297 // draw active frame
298 rgb_color black = (rgb_color){ 0, 0, 0, 255 };
299 drawingEngine->StrokeRect(rect, black);
300 } else if (index == fSelectedWorkspace) {
301 rgb_color gray = (rgb_color){ 80, 80, 80, 255 };
302 drawingEngine->StrokeRect(rect, gray);
305 rect.InsetBy(1, 1);
307 rgb_color color = workspace.Color();
308 if (!workspaceActive)
309 _DarkenColor(color);
311 // draw windows
313 BRegion backgroundRegion = redraw;
315 // TODO: would be nice to get the real update region here
317 BRect screenFrame = _ScreenFrame(index);
319 BRegion workspaceRegion(rect);
320 backgroundRegion.IntersectWith(&workspaceRegion);
321 drawingEngine->ConstrainClippingRegion(&backgroundRegion);
323 ServerFont font = fDrawState->Font();
324 font.SetSize(fWindow->ServerWindow()->App()->PlainFont().Size());
325 float reducedSize = ceilf(max_c(8.0f,
326 min_c(Frame().Height(), Frame().Width()) / 15));
327 if (font.Size() > reducedSize)
328 font.SetSize(reducedSize);
329 fDrawState->SetFont(font);
330 drawingEngine->SetFont(font);
332 // We draw from top down and cut the window out of the clipping region
333 // which reduces the flickering
334 ::Window* window;
335 BPoint leftTop;
336 while (workspace.GetPreviousWindow(window, leftTop) == B_OK) {
337 _DrawWindow(drawingEngine, rect, screenFrame, window,
338 leftTop, backgroundRegion, workspaceActive);
341 // draw background
342 drawingEngine->FillRect(rect, color);
344 drawingEngine->ConstrainClippingRegion(&redraw);
348 void
349 WorkspacesView::_DarkenColor(rgb_color& color) const
351 color = tint_color(color, B_DARKEN_2_TINT);
355 void
356 WorkspacesView::_Invalidate() const
358 BRect frame = Bounds();
359 LocalToScreenTransform().Apply(&frame);
361 BRegion region(frame);
362 Window()->MarkContentDirty(region);
366 void
367 WorkspacesView::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
368 BRegion* windowContentClipping, bool deep)
370 // we can only draw within our own area
371 BRegion redraw(ScreenAndUserClipping(windowContentClipping));
372 // add the current clipping
373 redraw.IntersectWith(effectiveClipping);
375 int32 columns, rows;
376 _GetGrid(columns, rows);
378 // draw grid
380 // make sure the grid around the active workspace is not drawn
381 // to reduce flicker
382 BRect activeRect = _WorkspaceAt(Window()->Desktop()->CurrentWorkspace());
383 BRegion gridRegion(redraw);
384 gridRegion.Exclude(activeRect);
385 drawingEngine->ConstrainClippingRegion(&gridRegion);
387 BRect frame = Bounds();
388 LocalToScreenTransform().Apply(&frame);
390 // horizontal lines
392 drawingEngine->StrokeLine(BPoint(frame.left, frame.top),
393 BPoint(frame.right, frame.top), ViewColor());
395 for (int32 row = 0; row < rows; row++) {
396 BRect rect = _WorkspaceAt(row * columns);
397 drawingEngine->StrokeLine(BPoint(frame.left, rect.bottom),
398 BPoint(frame.right, rect.bottom), ViewColor());
401 // vertical lines
403 drawingEngine->StrokeLine(BPoint(frame.left, frame.top),
404 BPoint(frame.left, frame.bottom), ViewColor());
406 for (int32 column = 0; column < columns; column++) {
407 BRect rect = _WorkspaceAt(column);
408 drawingEngine->StrokeLine(BPoint(rect.right, frame.top),
409 BPoint(rect.right, frame.bottom), ViewColor());
412 drawingEngine->ConstrainClippingRegion(&redraw);
414 // draw workspaces
416 for (int32 i = rows * columns; i-- > 0;) {
417 _DrawWorkspace(drawingEngine, redraw, i);
419 fWindow->ServerWindow()->ResyncDrawState();
423 void
424 WorkspacesView::MouseDown(BMessage* message, BPoint where)
426 // reset tracking variables
427 fSelectedWorkspace = -1;
428 fSelectedWindow = NULL;
429 fHasMoved = false;
431 // check if the correct mouse button is pressed
432 int32 buttons;
433 if (message->FindInt32("buttons", &buttons) != B_OK
434 || (buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
435 return;
437 int32 index;
438 BRect workspaceFrame = _WorkspaceAt(where, index);
439 if (index < 0)
440 return;
442 Workspace workspace(*Window()->Desktop(), index);
443 workspaceFrame.InsetBy(1, 1);
445 BRect screenFrame = _ScreenFrame(index);
447 ::Window* window;
448 BRect windowFrame;
449 BPoint leftTop;
450 while (workspace.GetPreviousWindow(window, leftTop) == B_OK) {
451 if (window->IsMinimized() || window->IsHidden())
452 continue;
454 BRect frame = _WindowFrame(workspaceFrame, screenFrame, window->Frame(),
455 leftTop);
456 if (frame.Contains(where) && window->Feel() != kDesktopWindowFeel
457 && window->Feel() != kWindowScreenFeel) {
458 fSelectedWindow = window;
459 windowFrame = frame;
460 break;
464 // Some special functionality (clicked with modifiers)
466 int32 modifiers;
467 if (fSelectedWindow != NULL
468 && message->FindInt32("modifiers", &modifiers) == B_OK) {
469 if ((modifiers & B_CONTROL_KEY) != 0) {
470 // Activate window if clicked with the control key pressed,
471 // minimize it if control+shift - this mirrors Deskbar
472 // shortcuts (when pressing a team menu item).
473 if ((modifiers & B_SHIFT_KEY) != 0)
474 fSelectedWindow->ServerWindow()->NotifyMinimize(true);
475 else
476 Window()->Desktop()->ActivateWindow(fSelectedWindow);
477 fSelectedWindow = NULL;
478 } else if ((modifiers & B_OPTION_KEY) != 0) {
479 // Also, send window to back if clicked with the option
480 // key pressed.
481 Window()->Desktop()->SendWindowBehind(fSelectedWindow);
482 fSelectedWindow = NULL;
486 // If this window is movable, we keep it selected
487 // (we prevent our own window from being moved, too)
489 if ((fSelectedWindow != NULL
490 && (fSelectedWindow->Flags() & B_NOT_MOVABLE) != 0)
491 || fSelectedWindow == Window()) {
492 fSelectedWindow = NULL;
495 fLeftTopOffset = where - windowFrame.LeftTop();
496 fSelectedWorkspace = index;
497 fClickPoint = where;
499 if (index >= 0)
500 _Invalidate();
504 void
505 WorkspacesView::MouseUp(BMessage* message, BPoint where)
507 if (!fHasMoved && fSelectedWorkspace >= 0) {
508 int32 index;
509 _WorkspaceAt(where, index);
510 if (index >= 0)
511 Window()->Desktop()->SetWorkspaceAsync(index);
514 if (fSelectedWindow != NULL) {
515 // We need to hide the selection frame again
516 _Invalidate();
519 fSelectedWindow = NULL;
520 fSelectedWorkspace = -1;
524 void
525 WorkspacesView::MouseMoved(BMessage* message, BPoint where)
527 if (fSelectedWindow == NULL && fSelectedWorkspace < 0)
528 return;
530 // check if the correct mouse button is pressed
531 int32 buttons;
532 if (message->FindInt32("buttons", &buttons) != B_OK
533 || (buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
534 return;
536 if (!fHasMoved) {
537 Window()->Desktop()->SetMouseEventWindow(Window());
538 // don't let us off the mouse
541 int32 index;
542 BRect workspaceFrame = _WorkspaceAt(where, index);
544 if (fSelectedWindow == NULL) {
545 if (fSelectedWorkspace >= 0 && fSelectedWorkspace != index) {
546 fSelectedWorkspace = index;
547 _Invalidate();
549 return;
552 workspaceFrame.InsetBy(1, 1);
554 if (index != fSelectedWorkspace) {
555 if (fSelectedWindow->IsNormal() && !fSelectedWindow->InWorkspace(index)) {
556 // move window to this new workspace
557 uint32 newWorkspaces = (fSelectedWindow->Workspaces()
558 & ~(1UL << fSelectedWorkspace)) | (1UL << index);
560 Window()->Desktop()->SetWindowWorkspaces(fSelectedWindow,
561 newWorkspaces);
563 fSelectedWorkspace = index;
566 BRect screenFrame = _ScreenFrame(index);
567 float left = rintf((where.x - workspaceFrame.left - fLeftTopOffset.x)
568 * screenFrame.Width() / workspaceFrame.Width());
569 float top = rintf((where.y - workspaceFrame.top - fLeftTopOffset.y)
570 * screenFrame.Height() / workspaceFrame.Height());
572 BPoint leftTop;
573 if (fSelectedWorkspace == Window()->Desktop()->CurrentWorkspace())
574 leftTop = fSelectedWindow->Frame().LeftTop();
575 else {
576 if (fSelectedWindow->Anchor(fSelectedWorkspace).position
577 == kInvalidWindowPosition) {
578 fSelectedWindow->Anchor(fSelectedWorkspace).position
579 = fSelectedWindow->Frame().LeftTop();
581 leftTop = fSelectedWindow->Anchor(fSelectedWorkspace).position;
584 // Don't treat every little mouse move as a window move - this would
585 // make it too hard to activate a workspace.
586 float diff = max_c(fabs(fClickPoint.x - where.x),
587 fabs(fClickPoint.y - where.y));
588 if (!fHasMoved && diff > 2)
589 fHasMoved = true;
591 if (fHasMoved) {
592 Window()->Desktop()->MoveWindowBy(fSelectedWindow, left - leftTop.x,
593 top - leftTop.y, fSelectedWorkspace);
598 void
599 WorkspacesView::WindowChanged(::Window* window)
601 // TODO: be smarter about this!
602 _Invalidate();
606 void
607 WorkspacesView::WindowRemoved(::Window* window)
609 if (fSelectedWindow == window)
610 fSelectedWindow = NULL;