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 * Copyright 2001-2005, Haiku.
// * Distributed under the terms of the MIT License.
// Permission is hereby granted, free of charge, to any person obtaining a *
// copy of this software and associated documentation files (the "Software"), * Authors:
// to deal in the Software without restriction, including without limitation * Jeremy Rand ([email protected])
// the rights to use, copy, modify, merge, publish, distribute, sublicense, * Jérôme Duval
// and/or sell copies of the Software, and to permit persons to whom the * Axel Dörfler
// 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.
//------------------------------------------------------------------------------
// Standard Includes -----------------------------------------------------------
#include <string.h>
// System Includes -------------------------------------------------------------
#include <Deskbar.h> #include <Deskbar.h>
#include <Messenger.h> #include <Messenger.h>
#include <Message.h> #include <Message.h>
@@ -37,338 +17,310 @@
#include <InterfaceDefs.h> #include <InterfaceDefs.h>
#include <Node.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 *kDeskbarSignature = "application/x-vnd.Be-TSKB";
static const char *gDeskbarSignature = "application/x-vnd.Be-TSKB";
static const uint32 gAddItemViewWhat = 'icon'; static const uint32 kMsgAddView = 'icon';
static const uint32 gAddItemAddonWhat = 'adon'; static const uint32 kMsgAddAddOn = 'adon';
static const uint32 gHasItemWhat = 'exst'; static const uint32 kMsgHasItem = 'exst';
static const uint32 gGetItemInfoWhat = 'info'; static const uint32 kMsgGetItemInfo = 'info';
static const uint32 gCountItemsWhat = 'cwnt'; static const uint32 kMsgCountItems = 'cwnt';
static const uint32 gRemoveItemWhat = 'remv'; static const uint32 kMsgRemoveItem = 'remv';
static const uint32 gLocationWhat = 'gloc'; static const uint32 kMsgLocation = 'gloc';
static const uint32 gIsExpandedWhat = 'gexp'; static const uint32 kMsgIsExpanded = 'gexp';
static const uint32 gSetLocationWhat = 'sloc'; static const uint32 kMsgSetLocation = 'sloc';
static const uint32 gExpandWhat = 'sexp'; static const uint32 kMsgExpand = 'sexp';
status_t get_deskbar_frame(BRect *frame) status_t
get_deskbar_frame(BRect *frame)
{ {
BMessenger deskbarMessenger(gDeskbarSignature); BMessenger deskbar(kDeskbarSignature);
BMessage requestMessage(B_GET_PROPERTY);
BMessage replyMessage;
status_t result; status_t result;
result = requestMessage.AddSpecifier("Frame"); BMessage request(B_GET_PROPERTY);
if (result == B_OK) { request.AddSpecifier("Frame");
result = requestMessage.AddSpecifier("Window", "Deskbar"); request.AddSpecifier("Window", "Deskbar");
if (result == B_OK) {
result = deskbarMessenger.SendMessage(&requestMessage, &replyMessage); BMessage reply;
if ((result == B_OK) && result = deskbar.SendMessage(&request, &reply);
(replyMessage.what == B_REPLY)) { if (result == B_OK)
result = replyMessage.FindRect("result", frame); result = reply.FindRect("result", frame);
}
} return result;
}
return(result);
} }
//------------------------------------------------------------------------------ // #pragma mark -
BDeskbar::BDeskbar() BDeskbar::BDeskbar()
: fMessenger(new BMessenger(gDeskbarSignature)) : fMessenger(new BMessenger(kDeskbarSignature))
{ {
} }
//------------------------------------------------------------------------------
BDeskbar::~BDeskbar() BDeskbar::~BDeskbar()
{ {
delete fMessenger; 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) && BRect
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) && BDeskbar::Frame(void) const
(replyMessage.what == B_REPLY)) { {
replyMessage.FindRect("result", &result); BRect frame(0.0, 0.0, 0.0, 0.0);
} get_deskbar_frame(&frame);
return(result);
} return frame;
//------------------------------------------------------------------------------ }
deskbar_location BDeskbar::Location(bool *isExpanded) const
{
BMessage requestMessage(gLocationWhat); deskbar_location
BMessage replyMessage; BDeskbar::Location(bool *_isExpanded) const
int32 result = 0; {
deskbar_location location = B_DESKBAR_RIGHT_TOP;
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) && BMessage request(kMsgLocation);
(replyMessage.what == 'rply') && BMessage reply;
(replyMessage.FindInt32("location", &result) == B_OK) &&
(isExpanded != NULL)) { if (_isExpanded)
replyMessage.FindBool("expanded", isExpanded); *_isExpanded = true;
}
return(static_cast<deskbar_location>(result)); if (fMessenger->IsTargetLocal()) {
} // ToDo: do something about this!
//------------------------------------------------------------------------------ // (if we just ask the Deskbar in this case, we would deadlock)
status_t BDeskbar::SetLocation(deskbar_location location, bool expanded) return location;
{ }
BMessage requestMessage(gSetLocationWhat);
BMessage replyMessage; if (fMessenger->SendMessage(&request, &reply) == B_OK) {
status_t result; int32 value;
if (reply.FindInt32("location", &value) == B_OK)
result = requestMessage.AddInt32("location", static_cast<int32>(location)); location = static_cast<deskbar_location>(value);
if (result == B_OK) {
result = requestMessage.AddBool("expand", expanded); if (_isExpanded
if (result == B_OK) { && reply.FindBool("expanded", _isExpanded) != B_OK)
result = fMessenger->SendMessage(&requestMessage, &replyMessage); *_isExpanded = true;
} }
}
return(result); return location;
} }
//------------------------------------------------------------------------------
bool BDeskbar::IsExpanded(void) const
{ status_t
BMessage requestMessage(gIsExpandedWhat); BDeskbar::SetLocation(deskbar_location location, bool expanded)
BMessage replyMessage; {
bool result = false; BMessage request(kMsgSetLocation);
request.AddInt32("location", static_cast<int32>(location));
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) && request.AddBool("expand", expanded);
(replyMessage.what == 'rply')) {
replyMessage.FindBool("expanded", &result); return fMessenger->SendMessage(&request);
} }
return(result);
}
//------------------------------------------------------------------------------ bool
status_t BDeskbar::Expand(bool expand) BDeskbar::IsExpanded(void) const
{ {
BMessage requestMessage(gExpandWhat); BMessage request(kMsgIsExpanded);
BMessage replyMessage; BMessage reply;
status_t result; bool isExpanded;
result = requestMessage.AddBool("expand", expand); if (fMessenger->SendMessage(&request, &reply) != B_OK
if (result == B_OK) { || reply.FindBool("expanded", &isExpanded) != B_OK)
result = fMessenger->SendMessage(&requestMessage, &replyMessage); isExpanded = true;
}
return(result); return isExpanded;
} }
//------------------------------------------------------------------------------
status_t BDeskbar::GetItemInfo(int32 id, const char **name) const
{ status_t
/* NOTE: Be's implementation returned B_BAD_VALUE if *name was NULL, BDeskbar::Expand(bool expand)
not just if name was NULL. This doesn't make much sense. Be's {
implementation means you cannot do the following: BMessage request(kMsgExpand);
request.AddBool("expand", expand);
const char *buffer = NULL;
myDeskbar.GetItemInfo(id, &buffer); return fMessenger->SendMessage(&request);
}
Instead, you are forced to write code that looks like:
char tmpBuf[10]; status_t
const char *buffer = tmpBuf; BDeskbar::GetItemInfo(int32 id, const char **_name) const
myDeskbar.GetItemInfo(id, &buffer); {
if (_name == NULL)
There are a couple of issues with this: return B_BAD_VALUE;
- 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 // Note: Be's implementation returns B_BAD_VALUE if *_name was NULL,
know whether the item's name will fit without overflowing the // not just if _name was NULL. This doesn't make much sense, so we
buffer. // do not imitate this behaviour.
- Worse, if the code looked like:
BMessage request(kMsgGetItemInfo);
const char *buffer = new char[5]; request.AddInt32("id", id);
myDeskbar.GetItemInfo(id, &buffer);
BMessage reply;
The code will result in a memory leak. The problem here is that status_t result = fMessenger->SendMessage(&request, &reply);
what buffer points to is changed by GetItemInfo(). If buffer if (result == B_OK) {
points to dynamically allocated memory, there is a good chance const char *name;
the result is a memory leak. result = reply.FindString("name", &name);
if (result == B_OK) {
The OpenBeOS implementation will allow *name to point to NULL or *_name = strdup(name);
non-NULL. If anything, we should consider forcing *name to point to if (*_name == NULL)
NULL for safety. result = B_NO_MEMORY;
*/ }
if (name == NULL) { }
return(B_BAD_VALUE); return result;
} }
BMessage requestMessage(gGetItemInfoWhat);
BMessage replyMessage; status_t
status_t result; BDeskbar::GetItemInfo(const char *name, int32 *_id) const
{
result = requestMessage.AddInt32("id", id); if (name == NULL)
if (result == B_OK) { return B_BAD_VALUE;
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
if (result == B_OK) { BMessage request(kMsgGetItemInfo);
const char *tmpName; request.AddString("name", name);
result = replyMessage.FindString("name", &tmpName);
if (result == B_OK) { BMessage reply;
*name = strdup(tmpName); status_t result = fMessenger->SendMessage(&request, &reply);
} if (result == B_OK)
} result = reply.FindInt32("id", _id);
}
return(result); return result;
} }
//------------------------------------------------------------------------------
status_t BDeskbar::GetItemInfo(const char *name, int32 *id) const
{ bool
if (name == NULL) { BDeskbar::HasItem(int32 id) const
return(B_BAD_VALUE); {
} BMessage request(kMsgHasItem);
request.AddInt32("id", id);
BMessage requestMessage(gGetItemInfoWhat);
BMessage replyMessage; BMessage reply;
status_t result; if (fMessenger->SendMessage(&request, &reply) == B_OK)
return reply.FindBool("exists");
result = requestMessage.AddString("name", name);
if (result == B_OK) { return false;
result = fMessenger->SendMessage(&requestMessage, &replyMessage); }
if (result == B_OK) {
result = replyMessage.FindInt32("id", id);
} bool
} BDeskbar::HasItem(const char *name) const
return(result); {
} BMessage request(kMsgHasItem);
//------------------------------------------------------------------------------ request.AddString("name", name);
bool BDeskbar::HasItem(int32 id) const
{ BMessage reply;
BMessage requestMessage(gHasItemWhat); if (fMessenger->SendMessage(&request, &reply) == B_OK)
BMessage replyMessage; return reply.FindBool("exists");
bool result = false;
return false;
if ((requestMessage.AddInt32("id", id) == B_OK) && }
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK)) {
replyMessage.FindBool("exists", &result);
} uint32
return(result); BDeskbar::CountItems(void) const
} {
//------------------------------------------------------------------------------ BMessage request(kMsgCountItems);
bool BDeskbar::HasItem(const char *name) const BMessage reply;
{
BMessage requestMessage(gHasItemWhat); if (fMessenger->SendMessage(&request, &reply) == B_OK)
BMessage replyMessage; return reply.FindInt32("count");
bool result = false;
return 0;
if ((requestMessage.AddString("name", name) == B_OK) && }
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK)) {
replyMessage.FindBool("exists", &result);
} status_t
return(result); BDeskbar::AddItem(BView *view, int32 *_id)
} {
//------------------------------------------------------------------------------ BMessage archive;
uint32 BDeskbar::CountItems(void) const status_t result = view->Archive(&archive);
{ if (result < B_OK)
BMessage requestMessage(gCountItemsWhat); return result;
BMessage replyMessage;
int32 result = 0; BMessage request(kMsgAddView);
request.AddMessage("view", &archive);
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) &&
(replyMessage.what == 'rply')) { BMessage reply;
replyMessage.FindInt32("count", &result); result = fMessenger->SendMessage(&request, &reply);
} if (result == B_OK) {
return(static_cast<uint32>(result)); if (_id != NULL)
} result = reply.FindInt32("id", _id);
//------------------------------------------------------------------------------ else
status_t BDeskbar::AddItem(BView *archivableView, int32 *id) reply.FindInt32("error", &result);
{ }
BMessage requestMessage(gAddItemViewWhat);
BMessage viewMessage; return result;
BMessage replyMessage; }
status_t result;
result = archivableView->Archive(&viewMessage); status_t
if (result == B_OK) { BDeskbar::AddItem(entry_ref *addon, int32 *_id)
result = requestMessage.AddMessage("view", &viewMessage); {
if (result == B_OK) { BMessage request(kMsgAddAddOn);
result = fMessenger->SendMessage(&requestMessage, &replyMessage); request.AddRef("addon", addon);
if ((result == B_OK) &&
(id != NULL)) { // Note: to make Deskbar items persistent, they need to have the attribute
result = replyMessage.FindInt32("id", id); // set. The Deskbar will remove the attribute automatically when needed.
} // ToDo: move this functionality into the Deskbar itself!
}
} BNode node;
return(result); status_t status = node.SetTo(addon);
} if (status < B_OK)
//------------------------------------------------------------------------------ return status;
status_t BDeskbar::AddItem(entry_ref *addon, int32 *id)
{ if ((status = node.WriteAttr("be:deskbar_item_status", B_STRING_TYPE,
BMessage requestMessage(gAddItemViewWhat); 0, "enabled", strlen("enabled"))) < B_OK)
BMessage replyMessage; return status;
status_t result;
BMessage reply;
result = requestMessage.AddRef("addon", addon); status = fMessenger->SendMessage(&request, &reply);
if (result == B_OK) { if (status == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage); if (_id != NULL)
if ((result == B_OK) && status = reply.FindInt32("id", _id);
(id != NULL)) { else
result = replyMessage.FindInt32("id", id); reply.FindInt32("error", &status);
}
/* 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. return status;
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 !!
*/ status_t
if (result == B_OK) { BDeskbar::RemoveItem(int32 id)
BNode node(addon); {
node.WriteAttr("be:deskbar_item_status", BMessage request(kMsgRemoveItem);
B_STRING_TYPE, 0, "enabled", strlen("enabled")); 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
return(result); // message was sent to the Deskbar
}
//------------------------------------------------------------------------------ return fMessenger->SendMessage(&request);
status_t BDeskbar::RemoveItem(int32 id) }
{
BMessage requestMessage(gRemoveItemWhat);
BMessage replyMessage; status_t
status_t result; BDeskbar::RemoveItem(const char *name)
{
result = requestMessage.AddInt32("id", id); BMessage request(kMsgRemoveItem);
if (result == B_OK) { request.AddString("name", name);
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
} // ToDo: the Deskbar does not reply to this message, so we don't
/* here R5 returns B_OK always (probably the result of SendMessage()) // know if it really succeeded - we can just acknowledge that the
* Deskbar itself also always return B_NO_REPLY, so ... // message was sent to the Deskbar
* here add some more checks for the future.
*/ return fMessenger->SendMessage(&request);
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);
}
//------------------------------------------------------------------------------