Almost rewrote BDeskbar. Fixed some bugs. Added some ToDo comments - the

functions will currently deadlock when called from within the Deskbar.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12962 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-06-06 14:23:06 +00:00
parent 457820a6ef
commit 860b32520c
+297 -345
View File
@@ -1,34 +1,14 @@
//------------------------------------------------------------------------------
// Copyright (c) 2001-2002, OpenBeOS
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// File Name: Deskbar.cpp
// Author: Jeremy Rand ([email protected]), Jérôme Duval
// Description: BDeskbar allows one to control the deskbar from an
// application.
//------------------------------------------------------------------------------
/*
* Copyright 2001-2005, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Rand ([email protected])
* Jérôme Duval
* Axel Dörfler
*/
// Standard Includes -----------------------------------------------------------
#include <string.h>
// System Includes -------------------------------------------------------------
#include <Deskbar.h>
#include <Messenger.h>
#include <Message.h>
@@ -37,338 +17,310 @@
#include <InterfaceDefs.h>
#include <Node.h>
// Project Includes ------------------------------------------------------------
#include <string.h>
// Local Includes --------------------------------------------------------------
// ToDo: in case the BDeskbar methods are called from a Deskbar add-on,
// they will currently deadlock most of the time (only those that do
// not need a reply will work).
// That should be fixed in the Deskbar itself, even if the Be API found
// a way around that (that doesn't work too well, BTW)
// Local Defines ---------------------------------------------------------------
// The API in this file should be considered as part of OpenTracker - but
// should work with all versions of Tracker available for Haiku.
// Globals ---------------------------------------------------------------------
static const char *gDeskbarSignature = "application/x-vnd.Be-TSKB";
static const uint32 gAddItemViewWhat = 'icon';
static const uint32 gAddItemAddonWhat = 'adon';
static const uint32 gHasItemWhat = 'exst';
static const uint32 gGetItemInfoWhat = 'info';
static const uint32 gCountItemsWhat = 'cwnt';
static const uint32 gRemoveItemWhat = 'remv';
static const uint32 gLocationWhat = 'gloc';
static const uint32 gIsExpandedWhat = 'gexp';
static const uint32 gSetLocationWhat = 'sloc';
static const uint32 gExpandWhat = 'sexp';
static const char *kDeskbarSignature = "application/x-vnd.Be-TSKB";
static const uint32 kMsgAddView = 'icon';
static const uint32 kMsgAddAddOn = 'adon';
static const uint32 kMsgHasItem = 'exst';
static const uint32 kMsgGetItemInfo = 'info';
static const uint32 kMsgCountItems = 'cwnt';
static const uint32 kMsgRemoveItem = 'remv';
static const uint32 kMsgLocation = 'gloc';
static const uint32 kMsgIsExpanded = 'gexp';
static const uint32 kMsgSetLocation = 'sloc';
static const uint32 kMsgExpand = 'sexp';
status_t get_deskbar_frame(BRect *frame)
status_t
get_deskbar_frame(BRect *frame)
{
BMessenger deskbarMessenger(gDeskbarSignature);
BMessage requestMessage(B_GET_PROPERTY);
BMessage replyMessage;
BMessenger deskbar(kDeskbarSignature);
status_t result;
result = requestMessage.AddSpecifier("Frame");
if (result == B_OK) {
result = requestMessage.AddSpecifier("Window", "Deskbar");
if (result == B_OK) {
result = deskbarMessenger.SendMessage(&requestMessage, &replyMessage);
if ((result == B_OK) &&
(replyMessage.what == B_REPLY)) {
result = replyMessage.FindRect("result", frame);
}
}
}
return(result);
BMessage request(B_GET_PROPERTY);
request.AddSpecifier("Frame");
request.AddSpecifier("Window", "Deskbar");
BMessage reply;
result = deskbar.SendMessage(&request, &reply);
if (result == B_OK)
result = reply.FindRect("result", frame);
return result;
}
//------------------------------------------------------------------------------
// #pragma mark -
BDeskbar::BDeskbar()
: fMessenger(new BMessenger(gDeskbarSignature))
: fMessenger(new BMessenger(kDeskbarSignature))
{
}
//------------------------------------------------------------------------------
BDeskbar::~BDeskbar()
{
delete fMessenger;
}
//------------------------------------------------------------------------------
BRect BDeskbar::Frame(void) const
{
BMessage requestMessage(B_GET_PROPERTY);
BMessage replyMessage;
BRect result(0.0, 0.0, 0.0, 0.0);
if ((requestMessage.AddSpecifier("Frame") == B_OK) &&
(requestMessage.AddSpecifier("Window", "Deskbar") == B_OK) &&
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) &&
(replyMessage.what == B_REPLY)) {
replyMessage.FindRect("result", &result);
}
return(result);
}
//------------------------------------------------------------------------------
deskbar_location BDeskbar::Location(bool *isExpanded) const
{
BMessage requestMessage(gLocationWhat);
BMessage replyMessage;
int32 result = 0;
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) &&
(replyMessage.what == 'rply') &&
(replyMessage.FindInt32("location", &result) == B_OK) &&
(isExpanded != NULL)) {
replyMessage.FindBool("expanded", isExpanded);
}
return(static_cast<deskbar_location>(result));
}
//------------------------------------------------------------------------------
status_t BDeskbar::SetLocation(deskbar_location location, bool expanded)
{
BMessage requestMessage(gSetLocationWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddInt32("location", static_cast<int32>(location));
if (result == B_OK) {
result = requestMessage.AddBool("expand", expanded);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
}
}
return(result);
}
//------------------------------------------------------------------------------
bool BDeskbar::IsExpanded(void) const
{
BMessage requestMessage(gIsExpandedWhat);
BMessage replyMessage;
bool result = false;
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) &&
(replyMessage.what == 'rply')) {
replyMessage.FindBool("expanded", &result);
}
return(result);
}
//------------------------------------------------------------------------------
status_t BDeskbar::Expand(bool expand)
{
BMessage requestMessage(gExpandWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddBool("expand", expand);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
}
return(result);
}
//------------------------------------------------------------------------------
status_t BDeskbar::GetItemInfo(int32 id, const char **name) const
{
/* NOTE: Be's implementation returned B_BAD_VALUE if *name was NULL,
not just if name was NULL. This doesn't make much sense. Be's
implementation means you cannot do the following:
const char *buffer = NULL;
myDeskbar.GetItemInfo(id, &buffer);
Instead, you are forced to write code that looks like:
char tmpBuf[10];
const char *buffer = tmpBuf;
myDeskbar.GetItemInfo(id, &buffer);
There are a couple of issues with this:
- Be's implementation does not use the space pointed to in buffer.
It cannot since it can't tell how big that space is and it won't
know whether the item's name will fit without overflowing the
buffer.
- Worse, if the code looked like:
const char *buffer = new char[5];
myDeskbar.GetItemInfo(id, &buffer);
The code will result in a memory leak. The problem here is that
what buffer points to is changed by GetItemInfo(). If buffer
points to dynamically allocated memory, there is a good chance
the result is a memory leak.
The OpenBeOS implementation will allow *name to point to NULL or
non-NULL. If anything, we should consider forcing *name to point to
NULL for safety.
*/
if (name == NULL) {
return(B_BAD_VALUE);
}
BMessage requestMessage(gGetItemInfoWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddInt32("id", id);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
if (result == B_OK) {
const char *tmpName;
result = replyMessage.FindString("name", &tmpName);
if (result == B_OK) {
*name = strdup(tmpName);
}
}
}
return(result);
}
//------------------------------------------------------------------------------
status_t BDeskbar::GetItemInfo(const char *name, int32 *id) const
{
if (name == NULL) {
return(B_BAD_VALUE);
}
BMessage requestMessage(gGetItemInfoWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddString("name", name);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
if (result == B_OK) {
result = replyMessage.FindInt32("id", id);
}
}
return(result);
}
//------------------------------------------------------------------------------
bool BDeskbar::HasItem(int32 id) const
{
BMessage requestMessage(gHasItemWhat);
BMessage replyMessage;
bool result = false;
if ((requestMessage.AddInt32("id", id) == B_OK) &&
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK)) {
replyMessage.FindBool("exists", &result);
}
return(result);
}
//------------------------------------------------------------------------------
bool BDeskbar::HasItem(const char *name) const
{
BMessage requestMessage(gHasItemWhat);
BMessage replyMessage;
bool result = false;
if ((requestMessage.AddString("name", name) == B_OK) &&
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK)) {
replyMessage.FindBool("exists", &result);
}
return(result);
}
//------------------------------------------------------------------------------
uint32 BDeskbar::CountItems(void) const
{
BMessage requestMessage(gCountItemsWhat);
BMessage replyMessage;
int32 result = 0;
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) &&
(replyMessage.what == 'rply')) {
replyMessage.FindInt32("count", &result);
}
return(static_cast<uint32>(result));
}
//------------------------------------------------------------------------------
status_t BDeskbar::AddItem(BView *archivableView, int32 *id)
{
BMessage requestMessage(gAddItemViewWhat);
BMessage viewMessage;
BMessage replyMessage;
status_t result;
result = archivableView->Archive(&viewMessage);
if (result == B_OK) {
result = requestMessage.AddMessage("view", &viewMessage);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
if ((result == B_OK) &&
(id != NULL)) {
result = replyMessage.FindInt32("id", id);
}
}
}
return(result);
}
//------------------------------------------------------------------------------
status_t BDeskbar::AddItem(entry_ref *addon, int32 *id)
{
BMessage requestMessage(gAddItemViewWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddRef("addon", addon);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
if ((result == B_OK) &&
(id != NULL)) {
result = replyMessage.FindInt32("id", id);
/* NOTE: I add this because the persistent state of the item
is not set by the Deskbar itself and it needs to be done somewhere.
In fact, telling the Deskbar about the addon ref is not enough and
adding this attribute is mandatory.
!! Deskbar seems to remove the attribute in RemoveItem though !!
*/
if (result == B_OK) {
BNode node(addon);
node.WriteAttr("be:deskbar_item_status",
B_STRING_TYPE, 0, "enabled", strlen("enabled"));
}
}
}
return(result);
}
//------------------------------------------------------------------------------
status_t BDeskbar::RemoveItem(int32 id)
{
BMessage requestMessage(gRemoveItemWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddInt32("id", id);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
}
/* here R5 returns B_OK always (probably the result of SendMessage())
* Deskbar itself also always return B_NO_REPLY, so ...
* here add some more checks for the future.
*/
if (result != B_OK)
return(result);
if (replyMessage.what == B_NO_REPLY)
return(B_OK); /* we can only speculate */
result = B_ERROR;
replyMessage.FindInt32("error", &result);
return(result);
}
//------------------------------------------------------------------------------
status_t BDeskbar::RemoveItem(const char *name)
{
BMessage requestMessage(gRemoveItemWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddString("name", name);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
}
/* same as above */
if (result != B_OK)
return(result);
if (replyMessage.what == B_NO_REPLY)
return(B_OK);
result = B_ERROR;
replyMessage.FindInt32("error", &result);
return(result);
}
//------------------------------------------------------------------------------
BRect
BDeskbar::Frame(void) const
{
BRect frame(0.0, 0.0, 0.0, 0.0);
get_deskbar_frame(&frame);
return frame;
}
deskbar_location
BDeskbar::Location(bool *_isExpanded) const
{
deskbar_location location = B_DESKBAR_RIGHT_TOP;
BMessage request(kMsgLocation);
BMessage reply;
if (_isExpanded)
*_isExpanded = true;
if (fMessenger->IsTargetLocal()) {
// ToDo: do something about this!
// (if we just ask the Deskbar in this case, we would deadlock)
return location;
}
if (fMessenger->SendMessage(&request, &reply) == B_OK) {
int32 value;
if (reply.FindInt32("location", &value) == B_OK)
location = static_cast<deskbar_location>(value);
if (_isExpanded
&& reply.FindBool("expanded", _isExpanded) != B_OK)
*_isExpanded = true;
}
return location;
}
status_t
BDeskbar::SetLocation(deskbar_location location, bool expanded)
{
BMessage request(kMsgSetLocation);
request.AddInt32("location", static_cast<int32>(location));
request.AddBool("expand", expanded);
return fMessenger->SendMessage(&request);
}
bool
BDeskbar::IsExpanded(void) const
{
BMessage request(kMsgIsExpanded);
BMessage reply;
bool isExpanded;
if (fMessenger->SendMessage(&request, &reply) != B_OK
|| reply.FindBool("expanded", &isExpanded) != B_OK)
isExpanded = true;
return isExpanded;
}
status_t
BDeskbar::Expand(bool expand)
{
BMessage request(kMsgExpand);
request.AddBool("expand", expand);
return fMessenger->SendMessage(&request);
}
status_t
BDeskbar::GetItemInfo(int32 id, const char **_name) const
{
if (_name == NULL)
return B_BAD_VALUE;
// Note: Be's implementation returns B_BAD_VALUE if *_name was NULL,
// not just if _name was NULL. This doesn't make much sense, so we
// do not imitate this behaviour.
BMessage request(kMsgGetItemInfo);
request.AddInt32("id", id);
BMessage reply;
status_t result = fMessenger->SendMessage(&request, &reply);
if (result == B_OK) {
const char *name;
result = reply.FindString("name", &name);
if (result == B_OK) {
*_name = strdup(name);
if (*_name == NULL)
result = B_NO_MEMORY;
}
}
return result;
}
status_t
BDeskbar::GetItemInfo(const char *name, int32 *_id) const
{
if (name == NULL)
return B_BAD_VALUE;
BMessage request(kMsgGetItemInfo);
request.AddString("name", name);
BMessage reply;
status_t result = fMessenger->SendMessage(&request, &reply);
if (result == B_OK)
result = reply.FindInt32("id", _id);
return result;
}
bool
BDeskbar::HasItem(int32 id) const
{
BMessage request(kMsgHasItem);
request.AddInt32("id", id);
BMessage reply;
if (fMessenger->SendMessage(&request, &reply) == B_OK)
return reply.FindBool("exists");
return false;
}
bool
BDeskbar::HasItem(const char *name) const
{
BMessage request(kMsgHasItem);
request.AddString("name", name);
BMessage reply;
if (fMessenger->SendMessage(&request, &reply) == B_OK)
return reply.FindBool("exists");
return false;
}
uint32
BDeskbar::CountItems(void) const
{
BMessage request(kMsgCountItems);
BMessage reply;
if (fMessenger->SendMessage(&request, &reply) == B_OK)
return reply.FindInt32("count");
return 0;
}
status_t
BDeskbar::AddItem(BView *view, int32 *_id)
{
BMessage archive;
status_t result = view->Archive(&archive);
if (result < B_OK)
return result;
BMessage request(kMsgAddView);
request.AddMessage("view", &archive);
BMessage reply;
result = fMessenger->SendMessage(&request, &reply);
if (result == B_OK) {
if (_id != NULL)
result = reply.FindInt32("id", _id);
else
reply.FindInt32("error", &result);
}
return result;
}
status_t
BDeskbar::AddItem(entry_ref *addon, int32 *_id)
{
BMessage request(kMsgAddAddOn);
request.AddRef("addon", addon);
// Note: to make Deskbar items persistent, they need to have the attribute
// set. The Deskbar will remove the attribute automatically when needed.
// ToDo: move this functionality into the Deskbar itself!
BNode node;
status_t status = node.SetTo(addon);
if (status < B_OK)
return status;
if ((status = node.WriteAttr("be:deskbar_item_status", B_STRING_TYPE,
0, "enabled", strlen("enabled"))) < B_OK)
return status;
BMessage reply;
status = fMessenger->SendMessage(&request, &reply);
if (status == B_OK) {
if (_id != NULL)
status = reply.FindInt32("id", _id);
else
reply.FindInt32("error", &status);
}
return status;
}
status_t
BDeskbar::RemoveItem(int32 id)
{
BMessage request(kMsgRemoveItem);
request.AddInt32("id", id);
// ToDo: the Deskbar does not reply to this message, so we don't
// know if it really succeeded - we can just acknowledge that the
// message was sent to the Deskbar
return fMessenger->SendMessage(&request);
}
status_t
BDeskbar::RemoveItem(const char *name)
{
BMessage request(kMsgRemoveItem);
request.AddString("name", name);
// ToDo: the Deskbar does not reply to this message, so we don't
// know if it really succeeded - we can just acknowledge that the
// message was sent to the Deskbar
return fMessenger->SendMessage(&request);
}