From dc3bd077cdc51f24fccc87187916388a2a7a835f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 25 May 2011 23:19:15 +0000 Subject: [PATCH] * Early work in progress of toying around with the idea to show some useful panels when touching the screen border with the mouse while holding down the CTRL key. * Right now, a simple running applications list is shown on the top/bottom, and a list of the windows of the current app on the left/right edge which you can click to front. * Imagine you could configure this to show various panels like a shelf, a query window, workspaces, recent files, favourite files, folder contents, have a clipboard for files, ... * Just wanted to have it in SVN even if it's only barely useful right now - I would be glad to hear your opinions on this, and if it's worthwhile to continue working on this. -alpha git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41754 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/Jamfile | 1 + src/apps/switcher/ApplicationsView.cpp | 74 +++++ src/apps/switcher/ApplicationsView.h | 26 ++ src/apps/switcher/CaptureWindow.cpp | 226 ++++++++++++++ src/apps/switcher/CaptureWindow.h | 27 ++ src/apps/switcher/GroupListView.cpp | 245 +++++++++++++++ src/apps/switcher/GroupListView.h | 90 ++++++ src/apps/switcher/Jamfile | 16 + src/apps/switcher/LaunchButton.cpp | 397 +++++++++++++++++++++++++ src/apps/switcher/LaunchButton.h | 86 ++++++ src/apps/switcher/PanelWindow.cpp | 183 ++++++++++++ src/apps/switcher/PanelWindow.h | 32 ++ src/apps/switcher/Switcher.cpp | 97 ++++++ src/apps/switcher/Switcher.h | 66 ++++ src/apps/switcher/Switcher.rdef | 18 ++ src/apps/switcher/WindowsView.cpp | 356 ++++++++++++++++++++++ src/apps/switcher/WindowsView.h | 32 ++ 17 files changed, 1972 insertions(+) create mode 100644 src/apps/switcher/ApplicationsView.cpp create mode 100644 src/apps/switcher/ApplicationsView.h create mode 100644 src/apps/switcher/CaptureWindow.cpp create mode 100644 src/apps/switcher/CaptureWindow.h create mode 100644 src/apps/switcher/GroupListView.cpp create mode 100644 src/apps/switcher/GroupListView.h create mode 100644 src/apps/switcher/Jamfile create mode 100644 src/apps/switcher/LaunchButton.cpp create mode 100644 src/apps/switcher/LaunchButton.h create mode 100644 src/apps/switcher/PanelWindow.cpp create mode 100644 src/apps/switcher/PanelWindow.h create mode 100644 src/apps/switcher/Switcher.cpp create mode 100644 src/apps/switcher/Switcher.h create mode 100644 src/apps/switcher/Switcher.rdef create mode 100644 src/apps/switcher/WindowsView.cpp create mode 100644 src/apps/switcher/WindowsView.h diff --git a/src/apps/Jamfile b/src/apps/Jamfile index c73a9cd445..6b69d67a87 100644 --- a/src/apps/Jamfile +++ b/src/apps/Jamfile @@ -55,6 +55,7 @@ HaikuSubInclude showimage ; HaikuSubInclude soundrecorder ; HaikuSubInclude stylededit ; HaikuSubInclude sudoku ; +HaikuSubInclude switcher ; HaikuSubInclude terminal ; HaikuSubInclude text_search ; HaikuSubInclude tracker ; diff --git a/src/apps/switcher/ApplicationsView.cpp b/src/apps/switcher/ApplicationsView.cpp new file mode 100644 index 0000000000..1be9ca8c2f --- /dev/null +++ b/src/apps/switcher/ApplicationsView.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "ApplicationsView.h" + +#include "LaunchButton.h" +#include "Switcher.h" + + +static const uint32 kMsgActivateApp = 'AcAp'; + + +ApplicationsView::ApplicationsView(uint32 location) + : + BGroupView((location & (kTopEdge | kBottomEdge)) != 0 + ? B_HORIZONTAL : B_VERTICAL) +{ +} + + +ApplicationsView::~ApplicationsView() +{ +} + + +void +ApplicationsView::AttachedToWindow() +{ + // TODO: make this dynamic! + + BList teamList; + be_roster->GetAppList(&teamList); + + for (int32 i = 0; i < teamList.CountItems(); i++) { + app_info appInfo; + team_id team = (team_id)teamList.ItemAt(i); + if (be_roster->GetRunningAppInfo(team, &appInfo) == B_OK) + _AddTeam(appInfo); + } +} + + +void +ApplicationsView::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgActivateApp: + be_roster->ActivateApp(message->FindInt32("team")); + break; + + default: + BGroupView::MessageReceived(message); + break; + } +} + + +void +ApplicationsView::_AddTeam(app_info& info) +{ + if ((info.flags & B_BACKGROUND_APP) != 0) + return; + + BMessage* message = new BMessage(kMsgActivateApp); + message->AddInt32("team", info.team); + + LaunchButton* button = new LaunchButton(info.signature, NULL, message, + this); + button->SetTo(&info.ref); + AddChild(button); +} diff --git a/src/apps/switcher/ApplicationsView.h b/src/apps/switcher/ApplicationsView.h new file mode 100644 index 0000000000..7c6a208423 --- /dev/null +++ b/src/apps/switcher/ApplicationsView.h @@ -0,0 +1,26 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef APPLICATIONS_VIEW_H +#define APPLICATIONS_VIEW_H + + +#include +#include + + +class ApplicationsView : public BGroupView { +public: + ApplicationsView(uint32 location); + virtual ~ApplicationsView(); + + virtual void AttachedToWindow(); + virtual void MessageReceived(BMessage* message); + +private: + void _AddTeam(app_info& info); +}; + + +#endif // APPLICATIONS_VIEW_H diff --git a/src/apps/switcher/CaptureWindow.cpp b/src/apps/switcher/CaptureWindow.cpp new file mode 100644 index 0000000000..93e2c22f62 --- /dev/null +++ b/src/apps/switcher/CaptureWindow.cpp @@ -0,0 +1,226 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "CaptureWindow.h" + +#include + +#include +#include + +#include "Switcher.h" + + +static const bigtime_t kUpdateDelay = 50000; + // 50 ms + + +class CaptureView : public BView { +public: + CaptureView(); + virtual ~CaptureView(); + + void AddMoveOutNotification(BMessage* message); + + virtual void MouseMoved(BPoint point, uint32 transit, + const BMessage* dragMessage); + virtual void KeyDown(const char* bytes, int32 numBytes); + +private: + void _UpdateLast(const BPoint& point); + void _Notify(uint32 location, team_id team); + void _SendMovedOutNotification(); + + static team_id _CurrentTeam(); + +private: + uint32 fModifierMask; + BPoint fLastPoint; + team_id fLastTeam; + bigtime_t fLastEvent; + + uint32 fMovedOutWhat; + BMessenger fMovedOutMessenger; + BRect fMovedOutFrame; +}; + + +CaptureView::CaptureView() + : + BView("main", 0), + fModifierMask(B_CONTROL_KEY), + fMovedOutWhat(0) +{ + SetEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS, B_NO_POINTER_HISTORY); + _UpdateLast(BPoint(-1, -1)); +} + + +CaptureView::~CaptureView() +{ +} + + +void +CaptureView::AddMoveOutNotification(BMessage* message) +{ + if (fMovedOutWhat != 0) + _SendMovedOutNotification(); + + if (message->FindMessenger("target", &fMovedOutMessenger) == B_OK + && message->FindRect("frame", &fMovedOutFrame) == B_OK) + fMovedOutWhat = (uint32)message->FindInt32("what"); +} + + +void +CaptureView::MouseMoved(BPoint point, uint32 transit, + const BMessage* dragMessage) +{ + ConvertToScreen(&point); + + if (fMovedOutWhat != 0 && !fMovedOutFrame.Contains(point)) + _SendMovedOutNotification(); + + uint32 modifiers = ::modifiers(); + if ((modifiers & fModifierMask) == 0) { + _UpdateLast(point); + return; + } + + // TODO: we will need to iterate over all existing screens to find the + // right one! + BScreen screen; + BRect screenFrame = screen.Frame(); + + uint32 location = kNowhere; + if (point.x <= screenFrame.left && fLastPoint.x > screenFrame.left) + location = kLeftEdge; + else if (point.x >= screenFrame.right && fLastPoint.x < screenFrame.right) + location = kRightEdge; + else if (point.y <= screenFrame.top && fLastPoint.y > screenFrame.top) + location = kTopEdge; + else if (point.y >= screenFrame.bottom && fLastPoint.y < screenFrame.bottom) + location = kBottomEdge; + + if (location != kNowhere) + _Notify(location, fLastTeam); + + _UpdateLast(point); +} + + +void +CaptureView::KeyDown(const char* bytes, int32 numBytes) +{ + if ((::modifiers() & (B_CONTROL_KEY | B_SHIFT_KEY | B_OPTION_KEY + | B_COMMAND_KEY)) != (B_COMMAND_KEY | B_CONTROL_KEY)) + return; + + uint32 location = kNowhere; + + switch (bytes[0]) { + case '1': + location = kLeftEdge; + break; + case '2': + location = kRightEdge; + break; + case '3': + location = kTopEdge; + break; + case '4': + location = kBottomEdge; + break; + } + + if (location != kNowhere) + _Notify(location, _CurrentTeam()); +} + + +void +CaptureView::_UpdateLast(const BPoint& point) +{ + fLastPoint = point; + + bigtime_t now = system_time(); + + // We update the currently active application only, if the mouse did + // not move over it for a certain time - this is necessary only for + // focus follow mouse. + if (now > fLastEvent + kUpdateDelay) + fLastTeam = _CurrentTeam(); + + fLastEvent = now; +} + + +void +CaptureView::_Notify(uint32 location, team_id team) +{ + if (location == kNowhere) + return; + + BMessage message(kMsgLocationTrigger); + message.AddInt32("location", location); + message.AddInt32("team", team); + + be_app->PostMessage(&message); +} + + +void +CaptureView::_SendMovedOutNotification() +{ + BMessage movedOut(fMovedOutWhat); + fMovedOutMessenger.SendMessage(&movedOut); + fMovedOutWhat = 0; +} + + +/*static*/ team_id +CaptureView::_CurrentTeam() +{ + app_info appInfo; + status_t status = be_roster->GetActiveAppInfo(&appInfo); + if (status == B_OK) + return appInfo.team; + + return status; +} + + +// #pragma mark - + + +CaptureWindow::CaptureWindow() + : + BWindow(BRect(0, 0, 100, 100), "mouse capture", B_NO_BORDER_WINDOW_LOOK, + B_NORMAL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES) +{ + fCaptureView = new CaptureView(); + AddChild(fCaptureView); +} + + +CaptureWindow::~CaptureWindow() +{ +} + + +void +CaptureWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgHideWhenMouseMovedOut: + fCaptureView->AddMoveOutNotification(message); + break; + default: + BWindow::MessageReceived(message); + break; + } +} diff --git a/src/apps/switcher/CaptureWindow.h b/src/apps/switcher/CaptureWindow.h new file mode 100644 index 0000000000..bfd6235c50 --- /dev/null +++ b/src/apps/switcher/CaptureWindow.h @@ -0,0 +1,27 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CAPTURE_WINDOW_H +#define CAPTURE_WINDOW_H + + +#include + + +class CaptureView; + + +class CaptureWindow : public BWindow { +public: + CaptureWindow(); + virtual ~CaptureWindow(); + + virtual void MessageReceived(BMessage* message); + +private: + CaptureView* fCaptureView; +}; + + +#endif // CAPTURE_WINDOW_H diff --git a/src/apps/switcher/GroupListView.cpp b/src/apps/switcher/GroupListView.cpp new file mode 100644 index 0000000000..370b671956 --- /dev/null +++ b/src/apps/switcher/GroupListView.cpp @@ -0,0 +1,245 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "GroupListView.h" + +#include + +#include + + +class RendererLayoutItem : public BAbstractLayout { +public: + RendererLayoutItem(BView* owner, int32 index, void* item, + ListItemRenderer*& renderer) + : + fOwner(owner), + fIndex(index), + fItem(item), + fRenderer(renderer) + { + } + + ListItemRenderer* Renderer() const + { + fRenderer->SetTo(fOwner, fItem); + return fRenderer; + } + + int32 Index() const + { + return fIndex; + } + + void* Item() const + { + return fItem; + } + + virtual BSize BaseMinSize() + { + fRenderer->SetTo(fOwner, fItem); + return fRenderer->MinSize(); + } + + virtual BSize BasePreferredSize() + { + fRenderer->SetTo(fOwner, fItem); + return fRenderer->PreferredSize(); + } + +protected: + virtual void DerivedLayoutItems() + { + // shouldn't ever be called? + } + +private: + BView* fOwner; + int32 fIndex; + void* fItem; + ListItemRenderer*& fRenderer; +}; + + +GroupListView::GroupListView(const char* name, GroupListModel* model, + enum orientation orientation, float spacing) + : + BView(NULL, B_WILL_DRAW, new BGroupLayout(orientation, spacing)), + fModel(NULL), + fItemRenderer(NULL), + fGroupRenderer(NULL), + fSelectionMessage(NULL) +{ + SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + SetModel(model); +} + + +GroupListView::~GroupListView() +{ + delete fModel; + delete fItemRenderer; + delete fGroupRenderer; + delete fSelectionMessage; +} + + +void +GroupListView::SetModel(GroupListModel* model) +{ + // TODO: remove all previous + // TODO: add change mechanism + // TODO: use a "virtual" BGroupLayout (ie. one that create its layout items + // on the fly). + fModel = model; + + std::map groupMap; + + int32 groupCount = model->CountGroups(); + for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) { + BGroupLayout* groupItem = new BGroupLayout(B_VERTICAL, 0); + groupItem->SetVisible(false); + AddChild(groupItem); + + void* group = model->GroupAt(groupIndex); + groupMap[(addr_t)group] = groupItem; + groupItem->AddItem(new RendererLayoutItem(this, groupIndex, group, + fGroupRenderer)); + } + + int32 itemCount = model->CountItems(); + for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) { + void* group = model->GroupForItemAt(itemIndex); + if (group == NULL) + continue; + + BGroupLayout* groupItem = groupMap[(addr_t)group]; + if (groupItem == NULL) + continue; + + groupItem->SetVisible(true); + + RendererLayoutItem* rendererItem = new RendererLayoutItem(this, + itemIndex, model->ItemAt(itemIndex), fItemRenderer); + groupItem->AddItem(rendererItem); + } +} + + +void +GroupListView::SetItemRenderer(ListItemRenderer* renderer) +{ + fItemRenderer = renderer; + InvalidateLayout(); +} + + +void +GroupListView::SetGroupRenderer(ListItemRenderer* renderer) +{ + fGroupRenderer = renderer; + InvalidateLayout(); +} + + +void +GroupListView::SetSelectionMessage(BMessage* message, BMessenger target) +{ + fSelectionMessage = message; + fSelectionTarget = target; +} + + +void +GroupListView::AttachedToWindow() +{ +} + + +void +GroupListView::MessageReceived(BMessage* message) +{ + switch (message->what) { + default: + BView::MessageReceived(message); + break; + } +} + + +void +GroupListView::MouseDown(BPoint point) +{ + if (fSelectionMessage == NULL) + return; + + BLayoutItem* item = _ItemAt(GetLayout(), point); + + if (RendererLayoutItem* rendererItem + = dynamic_cast(item)) { + BMessage message(*fSelectionMessage); + + int32 buttons = 0; + if (Window()->CurrentMessage() != NULL) + buttons = Window()->CurrentMessage()->FindInt32("buttons"); + if (buttons != 0) + message.AddInt32("buttons", buttons); + message.AddInt32("index", rendererItem->Index()); + message.AddPointer(rendererItem->Renderer() == fGroupRenderer + ? "group" : "item", rendererItem->Item()); + + fSelectionTarget.SendMessage(&message); + } +} + + +void +GroupListView::Draw(BRect updateRect) +{ + _Draw(GetLayout(), updateRect); +} + + +void +GroupListView::_Draw(BLayoutItem* item, BRect updateRect) +{ + if (RendererLayoutItem* rendererItem + = dynamic_cast(item)) { + ListItemRenderer* renderer = rendererItem->Renderer(); + renderer->Draw(this, rendererItem->Frame(), rendererItem->Index(), + false); + } else if (BLayout* layout = dynamic_cast(item)) { + for (int i = 0; i < layout->CountItems(); i++) { + item = layout->ItemAt(i); + if (!item->IsVisible() || !item->Frame().Intersects(updateRect)) + continue; + + _Draw(item, updateRect); + } + } +} + + +BLayoutItem* +GroupListView::_ItemAt(BLayoutItem* item, BPoint point) +{ + if (RendererLayoutItem* rendererItem + = dynamic_cast(item)) + return rendererItem; + + if (BLayout* layout = dynamic_cast(item)) { + for (int i = 0; i < layout->CountItems(); i++) { + item = layout->ItemAt(i); + if (!item->IsVisible() || !item->Frame().Contains(point)) + continue; + + return _ItemAt(item, point); + } + } + + return item; +} diff --git a/src/apps/switcher/GroupListView.h b/src/apps/switcher/GroupListView.h new file mode 100644 index 0000000000..4c49a0204b --- /dev/null +++ b/src/apps/switcher/GroupListView.h @@ -0,0 +1,90 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef GROUP_LIST_VIEW_H +#define GROUP_LIST_VIEW_H + + +#include +#include + + +class GroupListModel { +public: + virtual int32 CountItems() = 0; + virtual void* ItemAt(int32 index) = 0; + + virtual int32 CountGroups() = 0; + virtual void* GroupAt(int32 index) = 0; + virtual void* GroupForItemAt(int32 index) = 0; +}; + + +template +class TypedGroupListModel : public GroupListModel { +public: + virtual ItemType* ItemAt(int32 index) = 0; + + virtual GroupType* GroupAt(int32 index) = 0; + virtual GroupType* GroupForItemAt(int32 index) = 0; +}; + + +class ListItemRenderer { +public: + virtual void SetTo(BView* owner, void* item) = 0; + + virtual BSize MinSize() = 0; + virtual BSize MaxSize() = 0; + virtual BSize PreferredSize() = 0; + + virtual void Draw(BView* owner, BRect frame, int32 index, + bool selected) = 0; +}; + + +class GroupListView : public BView { +public: + GroupListView(const char* name, + GroupListModel* model = NULL, + enum orientation orientation = B_VERTICAL, + float spacing = 0); + virtual ~GroupListView(); + + GroupListModel* Model() const + { return fModel; } + virtual void SetModel(GroupListModel* model); + + ListItemRenderer* ItemRenderer() const + { return fItemRenderer; } + virtual void SetItemRenderer(ListItemRenderer* renderer); + ListItemRenderer* GroupRenderer() const + { return fGroupRenderer; } + virtual void SetGroupRenderer(ListItemRenderer* renderer); + + BMessage* SelectionMessage() const + { return fSelectionMessage; } + virtual void SetSelectionMessage(BMessage* message, + BMessenger target); + + virtual void AttachedToWindow(); + virtual void MessageReceived(BMessage* message); + virtual void MouseDown(BPoint point); + virtual void Draw(BRect updateRect); + +private: + void _Draw(BLayoutItem* item, BRect updateRect); + BLayoutItem* _ItemAt(BLayoutItem* item, BPoint point); + +private: + GroupListModel* fModel; + ListItemRenderer* fItemRenderer; + ListItemRenderer* fGroupRenderer; + + BMessage* fSelectionMessage; + BMessenger fSelectionTarget; +}; + + +#endif // GROUP_LIST_VIEW_H diff --git a/src/apps/switcher/Jamfile b/src/apps/switcher/Jamfile new file mode 100644 index 0000000000..c6c96f39cc --- /dev/null +++ b/src/apps/switcher/Jamfile @@ -0,0 +1,16 @@ +SubDir HAIKU_TOP src apps switcher ; + +UsePrivateHeaders app interface shared ; + +Application Switcher : + ApplicationsView.cpp + CaptureWindow.cpp + GroupListView.cpp + LaunchButton.cpp + PanelWindow.cpp + Switcher.cpp + WindowsView.cpp + + : be translation $(TARGET_LIBSUPC++) $(HAIKU_LOCALE_LIBS) libshared.a + : Switcher.rdef +; diff --git a/src/apps/switcher/LaunchButton.cpp b/src/apps/switcher/LaunchButton.cpp new file mode 100644 index 0000000000..a1a0ec29cf --- /dev/null +++ b/src/apps/switcher/LaunchButton.cpp @@ -0,0 +1,397 @@ +/* + * Copyright 2006-2009, Stephan Aßmus + * All rights reserved. Distributed under the terms of the MIT License. + */ + + +#include "LaunchButton.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEFAULT_ICON_SIZE 64 + +#undef B_TRANSLATE_CONTEXT +#define B_TRANSLATE_CONTEXT "LaunchBox" + + +static const float kDragStartDist = 10.0; +static const float kDragBitmapAlphaScale = 0.6; + + +bigtime_t LaunchButton::sClickSpeed = 0; +bool LaunchButton::sIgnoreDoubleClick = true; + + +LaunchButton::LaunchButton(const char* name, const char* label, + BMessage* message, BHandler* target) + : + BIconButton(name, label, message, target), + fRef(NULL), + fAppSig(NULL), + fDescription(""), + fAnticipatingDrop(false), + fLastClickTime(0), + fIconSize(DEFAULT_ICON_SIZE) +{ + if (sClickSpeed == 0 || get_click_speed(&sClickSpeed) != B_OK) + sClickSpeed = 500000; +} + + +LaunchButton::~LaunchButton() +{ + delete fRef; + free(fAppSig); +} + + +void +LaunchButton::AttachedToWindow() +{ + BIconButton::AttachedToWindow(); + _UpdateToolTip(); +} + + +void +LaunchButton::Draw(BRect updateRect) +{ + if (fAnticipatingDrop) { + rgb_color color = fRef ? ui_color(B_KEYBOARD_NAVIGATION_COLOR) + : (rgb_color){ 0, 130, 60, 255 }; + SetHighColor(color); + // limit clipping region to exclude the blue rect we just drew + BRect r(Bounds()); + StrokeRect(r); + r.InsetBy(1.0, 1.0); + BRegion region(r); + ConstrainClippingRegion(®ion); + } + if (IsValid()) { + BIconButton::Draw(updateRect); + } else { + rgb_color background = LowColor(); + rgb_color lightShadow = tint_color(background, + (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); + rgb_color shadow = tint_color(background, B_DARKEN_1_TINT); + rgb_color light = tint_color(background, B_LIGHTEN_1_TINT); + BRect r(Bounds()); + _DrawFrame(r, shadow, light, lightShadow, lightShadow); + r.InsetBy(2.0, 2.0); + SetHighColor(lightShadow); + FillRect(r); + } +} + + +void +LaunchButton::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_SIMPLE_DATA: + case B_REFS_RECEIVED: { + entry_ref ref; + if (message->FindRef("refs", &ref) == B_OK) { + if (fRef) { + if (ref != *fRef) { + BEntry entry(fRef, true); + if (entry.IsDirectory()) { + message->PrintToStream(); + // copy stuff into the directory + } else { + message->what = B_REFS_RECEIVED; + team_id team; + if (fAppSig) + team = be_roster->TeamFor(fAppSig); + else + team = be_roster->TeamFor(fRef); + if (team < 0) { + if (fAppSig) + be_roster->Launch(fAppSig, message, &team); + else + be_roster->Launch(fRef, message, &team); + } else { + app_info appInfo; + if (team >= 0 + && be_roster->GetRunningAppInfo(team, + &appInfo) == B_OK) { + BMessenger messenger(appInfo.signature, + team); + if (messenger.IsValid()) + messenger.SendMessage(message); + } + } + } + } + } else { + SetTo(&ref); + } + } + break; + } + case B_PASTE: + case B_MODIFIERS_CHANGED: + default: + BIconButton::MessageReceived(message); + break; + } +} + + +void +LaunchButton::MouseDown(BPoint where) +{ + bigtime_t now = system_time(); + bool callInherited = true; + if (sIgnoreDoubleClick && now - fLastClickTime < sClickSpeed) + callInherited = false; + fLastClickTime = now; + if (BMessage* message = Window()->CurrentMessage()) { + uint32 buttons; + message->FindInt32("buttons", (int32*)&buttons); + if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0 && IsInside()) { + // context menu? + } else + fDragStart = where; + } + if (callInherited) + BIconButton::MouseDown(where); +} + + +void +LaunchButton::MouseUp(BPoint where) +{ + if (fAnticipatingDrop) { + fAnticipatingDrop = false; + Invalidate(); + } + BIconButton::MouseUp(where); +} + + +void +LaunchButton::MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage) +{ + if ((dragMessage && (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW)) + && ((dragMessage->what == B_SIMPLE_DATA + || dragMessage->what == B_REFS_RECEIVED) || fRef)) { + if (!fAnticipatingDrop) { + fAnticipatingDrop = true; + Invalidate(); + } + } + if (!dragMessage || (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW)) { + if (fAnticipatingDrop) { + fAnticipatingDrop = false; + Invalidate(); + } + } + + BIconButton::MouseMoved(where, transit, dragMessage); +} + + +BSize +LaunchButton::MinSize() +{ + return PreferredSize(); +} + + +BSize +LaunchButton::PreferredSize() +{ + float minWidth = fIconSize; + float minHeight = fIconSize; + + float hPadding = max_c(6.0, ceilf(minHeight / 3.0)); + float vPadding = max_c(6.0, ceilf(minWidth / 3.0)); + + if (Label() != NULL && Label()[0] != '\0') { + font_height fh; + GetFontHeight(&fh); + minHeight += ceilf(fh.ascent + fh.descent) + vPadding; + minWidth += StringWidth(Label()) + vPadding; + } + + return BSize(minWidth + hPadding, minHeight + vPadding); +} + + +BSize +LaunchButton::MaxSize() +{ + return PreferredSize(); +} + + +// #pragma mark - + + +void +LaunchButton::SetTo(const entry_ref* ref) +{ + free(fAppSig); + fAppSig = NULL; + + delete fRef; + if (ref) { + fRef = new entry_ref(*ref); + // follow links + BEntry entry(fRef, true); + entry.GetRef(fRef); + + _UpdateIcon(fRef); + // see if this is an application + BFile file(ref, B_READ_ONLY); + BAppFileInfo info; + if (info.SetTo(&file) == B_OK) { + char mimeSig[B_MIME_TYPE_LENGTH]; + if (info.GetSignature(mimeSig) == B_OK) { + SetTo(mimeSig, false); + } else { + fprintf(stderr, "no MIME signature for '%s'\n", fRef->name); + } + } else { + fprintf(stderr, "no BAppFileInfo for '%s'\n", fRef->name); + } + } else { + fRef = NULL; + ClearIcon(); + } + _UpdateToolTip(); +} + + +entry_ref* +LaunchButton::Ref() const +{ + return fRef; +} + + +void +LaunchButton::SetTo(const char* appSig, bool updateIcon) +{ + if (appSig) { + free(fAppSig); + fAppSig = strdup(appSig); + if (updateIcon) { + entry_ref ref; + if (be_roster->FindApp(fAppSig, &ref) == B_OK) + SetTo(&ref); + } + } + _UpdateToolTip(); +} + + +void +LaunchButton::SetDescription(const char* text) +{ + fDescription.SetTo(text); + _UpdateToolTip(); +} + + +void +LaunchButton::SetIconSize(uint32 size) +{ + if (fIconSize == size) + return; + + fIconSize = size; + _UpdateIcon(fRef); + + InvalidateLayout(); + Invalidate(); +} + + +void +LaunchButton::SetIgnoreDoubleClick(bool refuse) +{ + sIgnoreDoubleClick = refuse; +} + + +// #pragma mark - + + +void +LaunchButton::_UpdateToolTip() +{ + // TODO: This works around a bug in the tool tip management. + // Remove when fixed (although no harm done...) + HideToolTip(); + SetToolTip(static_cast(NULL)); + + if (fRef) { + BString helper(fRef->name); + if (fDescription.CountChars() > 0) { + if (fDescription != helper) + helper << "\n\n" << fDescription.String(); + } else { + BFile file(fRef, B_READ_ONLY); + BAppFileInfo appFileInfo; + version_info info; + if (appFileInfo.SetTo(&file) == B_OK + && appFileInfo.GetVersionInfo(&info, + B_APP_VERSION_KIND) == B_OK + && strlen(info.short_info) > 0 + && helper.Compare(info.short_info) != 0) { + helper << "\n\n" << info.short_info; + } + } + SetToolTip(helper.String()); + } +} + + +void +LaunchButton::_UpdateIcon(const entry_ref* ref) +{ + BBitmap* icon = new BBitmap(BRect(0.0, 0.0, fIconSize - 1, + fIconSize - 1), B_RGBA32); + // NOTE: passing an invalid/unknown icon_size argument will cause + // the BNodeInfo to ignore it and just use the bitmap bounds. + if (BNodeInfo::GetTrackerIcon(ref, icon, (icon_size)fIconSize) == B_OK) + SetIcon(icon); + + delete icon; +} + + +void +LaunchButton::_DrawFrame(BRect r, rgb_color col1, rgb_color col2, + rgb_color col3, rgb_color col4) +{ + BeginLineArray(8); + AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col1); + AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col1); + AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col2); + AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col2); + r.InsetBy(1.0, 1.0); + AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col3); + AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col3); + AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col4); + AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col4); + EndLineArray(); +} diff --git a/src/apps/switcher/LaunchButton.h b/src/apps/switcher/LaunchButton.h new file mode 100644 index 0000000000..c9f95cf89c --- /dev/null +++ b/src/apps/switcher/LaunchButton.h @@ -0,0 +1,86 @@ +/* + * Copyright 2006-2011, Stephan Aßmus + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef LAUNCH_BUTTON_H +#define LAUNCH_BUTTON_H + + +#include +#include +#include + + +enum { + MSG_ADD_SLOT = 'adsl', + MSG_CLEAR_SLOT = 'clsl', + MSG_REMOVE_SLOT = 'rmsl', + MSG_LAUNCH = 'lnch', +}; + + +class LaunchButton : public BIconButton { +public: + LaunchButton(const char* name, + const char* label = NULL, + BMessage* message = NULL, + BHandler* target = NULL); + virtual ~LaunchButton(); + + // BIconButton interface + virtual void AttachedToWindow(); + virtual void Draw(BRect updateRect); + virtual void MessageReceived(BMessage* message); + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + virtual BSize MinSize(); + virtual BSize PreferredSize(); + virtual BSize MaxSize(); + + // LaunchButton + void SetTo(const entry_ref* ref); + entry_ref* Ref() const; + + void SetTo(const char* appSig, bool updateIcon); + const char* AppSignature() const + { return fAppSig; } + + void SetDescription(const char* text); + const char* Description() const + { return fDescription.String(); } + + void SetIconSize(uint32 size); + uint32 IconSize() const + { return fIconSize; } + + static void SetIgnoreDoubleClick(bool refuse); + static bool IgnoreDoubleClick() + { return sIgnoreDoubleClick; } + +private: + void _UpdateToolTip(); + void _UpdateIcon(const entry_ref* ref); + + void _DrawFrame(BRect frame, + rgb_color left, rgb_color top, + rgb_color right, rgb_color bottom); + +private: + entry_ref* fRef; + char* fAppSig; + BString fDescription; + + bool fAnticipatingDrop; + bigtime_t fLastClickTime; + BPoint fDragStart; + + uint32 fIconSize; + + static bigtime_t sClickSpeed; + static bool sIgnoreDoubleClick; +}; + +#endif // LAUNCH_BUTTON_H diff --git a/src/apps/switcher/PanelWindow.cpp b/src/apps/switcher/PanelWindow.cpp new file mode 100644 index 0000000000..f37c2b1f6a --- /dev/null +++ b/src/apps/switcher/PanelWindow.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PanelWindow.h" + +#include + +#include +#include +#include + +#include "ApplicationsView.h" +#include "GroupListView.h" +#include "Switcher.h" +#include "WindowsView.h" + + +static const uint32 kMsgShow = 'SHOW'; +static const uint32 kMsgHide = 'HIDE'; + +static const int32 kMinShowState = 0; +static const int32 kMaxShowState = 4; +static const bigtime_t kMoveDelay = 15000; + // 25 ms + + +PanelWindow::PanelWindow(uint32 location, uint32 which, team_id team) + : + BWindow(BRect(-16000, -16000, -15900, -15900), "panel", + B_BORDERED_WINDOW_LOOK, B_FLOATING_ALL_WINDOW_FEEL, + B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS | B_AVOID_FOCUS, + B_ALL_WORKSPACES), + fLocation(location), + fShowState(kMinShowState) +{ + SetLayout(new BGroupLayout(B_HORIZONTAL)); + + BView* child = _ViewFor(location, which, team); + if (child != NULL) + AddChild(child); + + Run(); + PostMessage(kMsgShow); +} + + +PanelWindow::~PanelWindow() +{ + BMessage message(kMsgLocationFree); + message.AddInt32("location", fLocation); + be_app->PostMessage(&message); +} + + +void +PanelWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgShow: + case kMsgHide: + _UpdateShowState(message->what); + break; + + default: + BWindow::MessageReceived(message); + break; + } +} + + +BView* +PanelWindow::_ViewFor(uint32 location, uint32 which, team_id team) const +{ + switch (which) { + case kShowApplications: + return new ApplicationsView(location); + case kShowApplicationWindows: + return new WindowsView(team, location); + + default: + return NULL; + } +} + + +void +PanelWindow::_UpdateShowState(uint32 how) +{ + if ((how == kMsgShow && fShowState >= kMaxShowState) + || (how == kMsgHide && fShowState <= kMinShowState)) + return; + + fShowState += how == kMsgShow ? 1 : -1; + + // Compute start and end position depending on the location + // TODO: multi-screen support + BScreen screen; + BRect screenFrame = screen.Frame(); + BPoint from; + BPoint to; + + switch (fLocation) { + case kLeftEdge: + case kRightEdge: + from.y = screenFrame.top + + (screenFrame.Height() - Bounds().Height()) / 2.f; + to.y = from.y; + break; + case kTopEdge: + case kBottomEdge: + from.x = screenFrame.left + + (screenFrame.Width() - Bounds().Width()) / 2.f; + to.x = from.x; + break; + } + + switch (fLocation) { + case kLeftEdge: + from.x = screenFrame.left - Bounds().Width(); + to.x = screenFrame.left; + break; + case kRightEdge: + from.x = screenFrame.right; + to.x = screenFrame.right - Bounds().Width(); + break; + case kTopEdge: + from.y = screenFrame.top - Bounds().Height(); + to.y = screenFrame.top; + break; + case kBottomEdge: + from.y = screenFrame.bottom; + to.y = screenFrame.bottom - Bounds().Height(); + break; + } + + MoveTo(from.x + _Factor() * (to.x - from.x), + from.y + _Factor() * (to.y - from.y)); + + if (kMsgShow && IsHidden()) + Show(); + else if (fShowState == 0 && kMsgHide && !IsHidden()) + Quit(); + + if ((how == kMsgShow && fShowState < kMaxShowState) + || (how == kMsgHide && fShowState > kMinShowState)) { + BMessage move(how); + BMessageRunner::StartSending(this, &move, kMoveDelay, 1); + } else if (how == kMsgShow) { + // Hide the window once the mouse left its frame + BMessage hide(kMsgHideWhenMouseMovedOut); + hide.AddMessenger("target", this); + hide.AddInt32("what", kMsgHide); + + // The window might not span over the whole screen, but one dimension + // should be ignored for the cursor movements + BRect frame = Frame(); + switch (fLocation) { + case kLeftEdge: + case kRightEdge: + frame.top = screenFrame.top; + frame.bottom = screenFrame.bottom; + break; + case kTopEdge: + case kBottomEdge: + frame.left = screenFrame.left; + frame.right = screenFrame.right; + break; + } + hide.AddRect("frame", frame); + be_app->PostMessage(&hide); + } +} + + +float +PanelWindow::_Factor() +{ + float factor = 1.f * fShowState / kMaxShowState; + return 1 - (factor - 1) * (factor - 1); +} diff --git a/src/apps/switcher/PanelWindow.h b/src/apps/switcher/PanelWindow.h new file mode 100644 index 0000000000..9df157ceb1 --- /dev/null +++ b/src/apps/switcher/PanelWindow.h @@ -0,0 +1,32 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PANEL_WINDOW_H +#define PANEL_WINDOW_H + + +#include + + +class PanelWindow : public BWindow { +public: + PanelWindow(uint32 location, uint32 which, + team_id team); + virtual ~PanelWindow(); + + virtual void MessageReceived(BMessage* message); + +private: + BView* _ViewFor(uint32 location, uint32 which, + team_id team) const; + void _UpdateShowState(uint32 how); + float _Factor(); + +private: + uint32 fLocation; + int32 fShowState; +}; + + +#endif // PANEL_WINDOW_H diff --git a/src/apps/switcher/Switcher.cpp b/src/apps/switcher/Switcher.cpp new file mode 100644 index 0000000000..9e047a3555 --- /dev/null +++ b/src/apps/switcher/Switcher.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Switcher.h" + +#include + +#include +#include + +#include "CaptureWindow.h" +#include "PanelWindow.h" + + +#undef B_TRANSLATE_CONTEXT +#define B_TRANSLATE_CONTEXT "Switcher" + + +const char* kSignature = "application/x-vnd.Haiku-Switcher"; + + +Switcher::Switcher() + : + BApplication(kSignature), + fOccupiedLocations(0) +{ +} + + +Switcher::~Switcher() +{ +} + + +void +Switcher::ReadyToRun() +{ + CaptureWindow* window = new CaptureWindow(); + window->Run(); + + fCaptureMessenger = window; +} + + +void +Switcher::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgLocationTrigger: + { + uint32 location = (uint32)message->FindInt32("location"); + if ((location & fOccupiedLocations) == 0) { + // TODO: make function configurable + uint32 which = kShowApplicationWindows; + if ((location & (kTopEdge | kBottomEdge)) != 0) + which = kShowApplications; + + new PanelWindow(location, which, + (team_id)message->FindInt32("team")); + fOccupiedLocations |= location; + } + break; + } + + case kMsgLocationFree: + { + uint32 location; + if (message->FindInt32("location", (int32*)&location) == B_OK) + fOccupiedLocations &= ~location; + break; + } + + case kMsgHideWhenMouseMovedOut: + fCaptureMessenger.SendMessage(message); + break; + + default: + BApplication::MessageReceived(message); + break; + } +} + + +// #pragma mark - + + +int +main(int /*argc*/, char** /*argv*/) +{ + Switcher app; + app.Run(); + + return 0; +} diff --git a/src/apps/switcher/Switcher.h b/src/apps/switcher/Switcher.h new file mode 100644 index 0000000000..5fd9713d4c --- /dev/null +++ b/src/apps/switcher/Switcher.h @@ -0,0 +1,66 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef SWITCHER_H +#define SWITCHER_H + + +#include + + +class BMessage; + + +enum { + kNowhere = 0, + kTopEdge = 0x01, + kBottomEdge = 0x02, + kLeftEdge = 0x04, + kRightEdge = 0x08, + // TODO: not yet supported + kTopLeftCorner = 0x10, + kTopRightCorner = 0x20, + kBottomLeftCorner = 0x40, + kBottomRightCorner = 0x80 +}; + +enum { + kShowApplications, + kShowApplicationWindows, + kShowWorkspaceWindows, + kShowAllWindows, + kShowWorkspaces, + kShowShelf, + kShowFavorites, + kShowRecentFiles, + kShowFilesClipboard, + kShowFolder, + kShowQuery +}; + + +static const uint32 kMsgLocationTrigger = 'LoTr'; +static const uint32 kMsgLocationFree = 'LoFr'; +static const uint32 kMsgHideWhenMouseMovedOut = 'HwMo'; + + +class Switcher : public BApplication { +public: + Switcher(); + virtual ~Switcher(); + + virtual void ReadyToRun(); + + virtual void MessageReceived(BMessage* message); + +private: + BMessenger fCaptureMessenger; + uint32 fOccupiedLocations; +}; + + +extern const char* kSignature; + + +#endif // SWITCHER_H diff --git a/src/apps/switcher/Switcher.rdef b/src/apps/switcher/Switcher.rdef new file mode 100644 index 0000000000..0b8a1ab95b --- /dev/null +++ b/src/apps/switcher/Switcher.rdef @@ -0,0 +1,18 @@ + +resource app_signature "application/x-vnd.Haiku-Switcher"; + +//resource app_name_catalog_entry "x-vnd.Haiku-Switcher:System name:Switcher"; + +resource app_version { + major = 1, + middle = 0, + minor = 0, + + variety = B_APPV_BETA, + internal = 0, + + short_info = "Switcher", + long_info = "Switcher ©2011 Haiku, Inc." +}; + +resource app_flags B_SINGLE_LAUNCH | B_BACKGROUND_APP; diff --git a/src/apps/switcher/WindowsView.cpp b/src/apps/switcher/WindowsView.cpp new file mode 100644 index 0000000000..8fa0dfdba9 --- /dev/null +++ b/src/apps/switcher/WindowsView.cpp @@ -0,0 +1,356 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "WindowsView.h" + +#include + +#include +#include +#include + +#include +#include +#include + +#include "GroupListView.h" +#include "LaunchButton.h" +#include "Switcher.h" + + +static const uint32 kMsgActivateWindow = 'AcWn'; + + +class WindowModel : public TypedGroupListModel { +public: + WindowModel(team_id team) + : + fWindows(true), + fWorkspaces(0), + fWorkspaceCount(0) + { + // TODO: more than one team via signature! + int32 count; + int32* tokens = get_token_list(team, &count); + + for (int32 i = 0; i < count; i++) { + client_window_info* info = get_window_info(tokens[i]); + if (!_WindowShouldBeListed(info)) { + free(info); + continue; + } + + fWorkspaces |= info->workspaces; + fWindows.AddItem(info); + } + free(tokens); + + for (uint32 i = 0; i < 32; i++) { + if ((fWorkspaces & (1UL << i)) != 0) + fWorkspaceCount++; + } + + fWindows.SortItems(&_CompareWindowInfo); + } + + virtual ~WindowModel() + { + } + + void BringToFront(int32 index) + { + client_window_info* info = fWindows.ItemAt(index); + if (info == NULL) + return; + + do_window_action(info->server_token, B_BRING_TO_FRONT, BRect(), false); + } + + void Close(int32 index) + { + client_window_info* info = fWindows.ItemAt(index); + if (info == NULL) + return; + + BMessenger window; + BMessenger::Private(window).SetTo(info->team, info->client_port, + info->client_token); + window.SendMessage(B_QUIT_REQUESTED); + } + + virtual int32 CountItems() + { + return fWindows.CountItems(); + } + + virtual client_window_info* ItemAt(int32 index) + { + return fWindows.ItemAt(index); + } + + virtual int32 CountGroups() + { + return fWorkspaceCount; + } + + virtual void* GroupAt(int32 index) + { + return (void*)(_NthSetBit(index, fWorkspaces) + 1); + } + + virtual void* GroupForItemAt(int32 index) + { + client_window_info* info = ItemAt(index); + return (void*)(_NthSetBit(0, info->workspaces) + 1); + } + +private: + bool _WindowShouldBeListed(client_window_info* info) + { + return info != NULL + && (info->feel == B_NORMAL_WINDOW_FEEL + || info->feel == kWindowScreenFeel) + && (info->show_hide_level <= 0 || info->is_mini); + } + + int32 _NthSetBit(int32 index, uint32 mask) + { + for (uint32 i = 0; i < 32; i++) { + if ((mask & (1UL << i)) != 0) { + if (index-- == 0) + return i; + } + } + return 0; + } + + static int _CompareWindowInfo(const client_window_info* a, + const client_window_info* b) + { + return strcasecmp(a->name, b->name); + } + +private: + BObjectList fWindows; + uint32 fWorkspaces; + int32 fWorkspaceCount; +}; + + +class StringItemRenderer : public ListItemRenderer { +public: + StringItemRenderer() + { + } + + virtual ~StringItemRenderer() + { + } + + void SetText(BView* owner, const BString& text) + { + fText = text; + owner->TruncateString(&fText, B_TRUNCATE_MIDDLE, 200); + SetWidth((int32)ceilf(owner->StringWidth(fText.String()))); + + font_height fontHeight; + owner->GetFontHeight(&fontHeight); + + SetBaselineOffset( + 2 + (int32)ceilf(fontHeight.ascent + fontHeight.leading / 2)); + + SetHeight((int32)ceilf(fontHeight.ascent) + + (int32)ceilf(fontHeight.descent) + + (int32)ceilf(fontHeight.leading) + 4); + } + + virtual void SetWidth(int32 width) + { + fWidth = width; + } + + virtual void SetHeight(int32 height) + { + fHeight = height; + } + + virtual void SetBaselineOffset(int32 offset) + { + fBaselineOffset = offset; + } + + const BString& Text() const + { + return fText; + } + + virtual BSize MinSize() + { + return BSize(fWidth, fHeight); + } + + virtual BSize MaxSize() + { + return BSize(B_SIZE_UNLIMITED, fHeight); + } + + virtual BSize PreferredSize() + { + return BSize(fWidth, fHeight); + } + + virtual void Draw(BView* owner, BRect frame, int32 index, bool selected) + { + owner->SetLowColor(owner->ViewColor()); + owner->MovePenTo(frame.left, frame.top + fBaselineOffset); + owner->DrawString(fText); + } + +private: + BString fText; + int32 fWidth; + int32 fHeight; + int32 fBaselineOffset; +}; + + +class WorkspaceRenderer : public StringItemRenderer { +public: + virtual void SetTo(BView* owner, void* item) + { + fWorkspace = (uint32)item; + + if ((uint32)current_workspace() == fWorkspace - 1) + SetText(owner, "Current workspace"); + else { + BString text("Workspace "); + text << fWorkspace; + SetText(owner, text); + } + } + + virtual void Draw(BView* owner, BRect frame, int32 index, bool selected) + { + owner->SetHighColor(tint_color(owner->ViewColor(), B_DARKEN_2_TINT)); + StringItemRenderer::Draw(owner, frame, index, false); + } + +private: + uint32 fWorkspace; +}; + + +class WindowRenderer : public StringItemRenderer { +public: + virtual void SetTo(BView* owner, void* item) + { + fInfo = (client_window_info*)item; + SetText(owner, fInfo->name); + } + + virtual void SetWidth(int32 width) + { + StringItemRenderer::SetWidth(width + 20); + } + + virtual void Draw(BView* owner, BRect frame, int32 index, bool selected) + { + owner->SetHighColor(0, 0, 0); + frame.left += 20; + StringItemRenderer::Draw(owner, frame, index, selected); + } + +private: + client_window_info* fInfo; +}; + + +// #pragma mark - + + +WindowsView::WindowsView(team_id team, uint32 location) + : + BGridView("windows") +{ + app_info info; + be_roster->GetRunningAppInfo(team, &info); + + LaunchButton* launchButton = new LaunchButton(info.signature, NULL, NULL, + this); + launchButton->SetTo(&info.ref); + + BStringView* nameView = new BStringView("name", info.ref.name); + BFont font(be_plain_font); + font.SetSize(font.Size() * 2); + font.SetFace(B_BOLD_FACE); + nameView->SetFont(&font); + + fListView = new GroupListView("list", new WindowModel(team), + _Orientation(location)); + fListView->SetItemRenderer(new WindowRenderer()); + fListView->SetGroupRenderer(new WorkspaceRenderer()); + + GridLayout()->SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, + B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING); + + if (_Orientation(location) == B_HORIZONTAL) { + BLayoutBuilder::Grid<>(this) + .Add(launchButton, 0, 0) + .Add(nameView, 1, 0) + .Add(fListView, 2, 0); + } else { + BLayoutBuilder::Grid<>(this) + .Add(launchButton, 0, 0) + .Add(nameView, 1, 0) + .Add(fListView, 0, 1, 2); + } +} + + +WindowsView::~WindowsView() +{ +} + + +void +WindowsView::AttachedToWindow() +{ + fListView->SetSelectionMessage(new BMessage(kMsgActivateWindow), this); +} + + +void +WindowsView::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgActivateWindow: + { + int32 index; + if (message->FindInt32("index", &index) == B_OK + && message->HasPointer("item")) { + WindowModel* model = (WindowModel*)fListView->Model(); + + if (message->FindInt32("buttons") == B_SECONDARY_MOUSE_BUTTON) + model->Close(index); + else + model->BringToFront(index); + } + break; + } + + default: + BGridView::MessageReceived(message); + break; + } +} + + +orientation +WindowsView::_Orientation(uint32 location) +{ + return (location & (kTopEdge | kBottomEdge)) != 0 + ? B_HORIZONTAL : B_VERTICAL; +} diff --git a/src/apps/switcher/WindowsView.h b/src/apps/switcher/WindowsView.h new file mode 100644 index 0000000000..69a94d9b4b --- /dev/null +++ b/src/apps/switcher/WindowsView.h @@ -0,0 +1,32 @@ +/* + * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef WINDOWS_VIEW_H +#define WINDOWS_VIEW_H + + +#include + + +class GroupListView; + + +class WindowsView : public BGridView { +public: + WindowsView(team_id team, uint32 location); + virtual ~WindowsView(); + +protected: + virtual void AttachedToWindow(); + virtual void MessageReceived(BMessage* message); + +private: + orientation _Orientation(uint32 location); + +private: + GroupListView* fListView; +}; + + +#endif // WINDOWS_VIEW_H