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
+264 -312
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);
BMessage replyMessage;
int32 result = 0;
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) &&
(replyMessage.what == 'rply') && deskbar_location
(replyMessage.FindInt32("location", &result) == B_OK) && BDeskbar::Location(bool *_isExpanded) const
(isExpanded != NULL)) { {
replyMessage.FindBool("expanded", isExpanded); 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;
} }
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 (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) { if (result == B_OK) {
result = requestMessage.AddBool("expand", expanded); const char *name;
result = reply.FindString("name", &name);
if (result == B_OK) { if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage); *_name = strdup(name);
if (*_name == NULL)
result = B_NO_MEMORY;
} }
} }
return(result); 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')) { status_t
replyMessage.FindBool("expanded", &result); BDeskbar::GetItemInfo(const char *name, int32 *_id) const
} {
return(result); 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;
} }
//------------------------------------------------------------------------------
status_t BDeskbar::Expand(bool expand)
{
BMessage requestMessage(gExpandWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddBool("expand", expand);
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 (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage); if (_id != NULL)
result = reply.FindInt32("id", _id);
else
reply.FindInt32("error", &result);
} }
return(result);
return result;
} }
//------------------------------------------------------------------------------
status_t BDeskbar::GetItemInfo(int32 id, const char **name) const
status_t
BDeskbar::AddItem(entry_ref *addon, int32 *_id)
{ {
/* NOTE: Be's implementation returned B_BAD_VALUE if *name was NULL, BMessage request(kMsgAddAddOn);
not just if name was NULL. This doesn't make much sense. Be's request.AddRef("addon", addon);
implementation means you cannot do the following:
const char *buffer = NULL; // Note: to make Deskbar items persistent, they need to have the attribute
myDeskbar.GetItemInfo(id, &buffer); // set. The Deskbar will remove the attribute automatically when needed.
// ToDo: move this functionality into the Deskbar itself!
Instead, you are forced to write code that looks like: BNode node;
status_t status = node.SetTo(addon);
if (status < B_OK)
return status;
char tmpBuf[10]; if ((status = node.WriteAttr("be:deskbar_item_status", B_STRING_TYPE,
const char *buffer = tmpBuf; 0, "enabled", strlen("enabled"))) < B_OK)
myDeskbar.GetItemInfo(id, &buffer); return status;
There are a couple of issues with this: BMessage reply;
- Be's implementation does not use the space pointed to in buffer. status = fMessenger->SendMessage(&request, &reply);
It cannot since it can't tell how big that space is and it won't if (status == B_OK) {
know whether the item's name will fit without overflowing the if (_id != NULL)
buffer. status = reply.FindInt32("id", _id);
- Worse, if the code looked like: else
reply.FindInt32("error", &status);
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); return status;
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
status_t
BDeskbar::RemoveItem(int32 id)
{ {
if (name == NULL) { BMessage request(kMsgRemoveItem);
return(B_BAD_VALUE); request.AddInt32("id", id);
}
BMessage requestMessage(gGetItemInfoWhat); // ToDo: the Deskbar does not reply to this message, so we don't
BMessage replyMessage; // know if it really succeeded - we can just acknowledge that the
status_t result; // message was sent to the Deskbar
result = requestMessage.AddString("name", name); return fMessenger->SendMessage(&request);
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
status_t
BDeskbar::RemoveItem(const char *name)
{ {
BMessage requestMessage(gHasItemWhat); BMessage request(kMsgRemoveItem);
BMessage replyMessage; request.AddString("name", name);
bool result = false;
if ((requestMessage.AddInt32("id", id) == B_OK) && // ToDo: the Deskbar does not reply to this message, so we don't
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK)) { // know if it really succeeded - we can just acknowledge that the
replyMessage.FindBool("exists", &result); // message was sent to the Deskbar
}
return(result);
}
//------------------------------------------------------------------------------
bool BDeskbar::HasItem(const char *name) const
{
BMessage requestMessage(gHasItemWhat);
BMessage replyMessage;
bool result = false;
if ((requestMessage.AddString("name", name) == B_OK) && return fMessenger->SendMessage(&request);
(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);
} }
//------------------------------------------------------------------------------