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
+242 -290
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
BRect
BDeskbar::Frame(void) const
{ {
BMessage requestMessage(B_GET_PROPERTY); BRect frame(0.0, 0.0, 0.0, 0.0);
BMessage replyMessage; get_deskbar_frame(&frame);
BRect result(0.0, 0.0, 0.0, 0.0);
if ((requestMessage.AddSpecifier("Frame") == B_OK) && return frame;
(requestMessage.AddSpecifier("Window", "Deskbar") == B_OK) &&
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) &&
(replyMessage.what == B_REPLY)) {
replyMessage.FindRect("result", &result);
} }
return(result);
}
//------------------------------------------------------------------------------ deskbar_location
deskbar_location BDeskbar::Location(bool *isExpanded) const BDeskbar::Location(bool *_isExpanded) const
{ {
BMessage requestMessage(gLocationWhat); deskbar_location location = B_DESKBAR_RIGHT_TOP;
BMessage replyMessage; BMessage request(kMsgLocation);
int32 result = 0; BMessage reply;
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) && if (_isExpanded)
(replyMessage.what == 'rply') && *_isExpanded = true;
(replyMessage.FindInt32("location", &result) == B_OK) &&
(isExpanded != NULL)) { if (fMessenger->IsTargetLocal()) {
replyMessage.FindBool("expanded", isExpanded); // 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));
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;
} }
//------------------------------------------------------------------------------
status_t BDeskbar::SetLocation(deskbar_location location, bool expanded) return location;
}
status_t
BDeskbar::SetLocation(deskbar_location location, bool expanded)
{ {
BMessage requestMessage(gSetLocationWhat); BMessage request(kMsgSetLocation);
BMessage replyMessage; request.AddInt32("location", static_cast<int32>(location));
status_t result; request.AddBool("expand", expanded);
result = requestMessage.AddInt32("location", static_cast<int32>(location)); return fMessenger->SendMessage(&request);
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
bool BDeskbar::IsExpanded(void) const
{ {
BMessage requestMessage(gIsExpandedWhat); BMessage request(kMsgIsExpanded);
BMessage replyMessage; BMessage reply;
bool result = false; bool isExpanded;
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) && if (fMessenger->SendMessage(&request, &reply) != B_OK
(replyMessage.what == 'rply')) { || reply.FindBool("expanded", &isExpanded) != B_OK)
replyMessage.FindBool("expanded", &result); isExpanded = true;
return isExpanded;
} }
return(result);
}
//------------------------------------------------------------------------------ status_t
status_t BDeskbar::Expand(bool expand) BDeskbar::Expand(bool expand)
{ {
BMessage requestMessage(gExpandWhat); BMessage request(kMsgExpand);
BMessage replyMessage; request.AddBool("expand", expand);
status_t result;
result = requestMessage.AddBool("expand", expand); return fMessenger->SendMessage(&request);
if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage);
} }
return(result);
}
//------------------------------------------------------------------------------ status_t
status_t BDeskbar::GetItemInfo(int32 id, const char **name) const BDeskbar::GetItemInfo(int32 id, const char **_name) const
{ {
/* NOTE: Be's implementation returned B_BAD_VALUE if *name was NULL, if (_name == NULL)
not just if name was NULL. This doesn't make much sense. Be's return B_BAD_VALUE;
implementation means you cannot do the following:
const char *buffer = NULL; // Note: Be's implementation returns B_BAD_VALUE if *_name was NULL,
myDeskbar.GetItemInfo(id, &buffer); // not just if _name was NULL. This doesn't make much sense, so we
// do not imitate this behaviour.
Instead, you are forced to write code that looks like: BMessage request(kMsgGetItemInfo);
request.AddInt32("id", id);
char tmpBuf[10]; BMessage reply;
const char *buffer = tmpBuf; status_t result = fMessenger->SendMessage(&request, &reply);
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) { if (result == B_OK) {
result = fMessenger->SendMessage(&requestMessage, &replyMessage); const char *name;
result = reply.FindString("name", &name);
if (result == B_OK) { if (result == B_OK) {
const char *tmpName; *_name = strdup(name);
result = replyMessage.FindString("name", &tmpName); if (*_name == NULL)
if (result == B_OK) { result = B_NO_MEMORY;
*name = strdup(tmpName);
} }
} }
return result;
} }
return(result);
}
//------------------------------------------------------------------------------ status_t
status_t BDeskbar::GetItemInfo(const char *name, int32 *id) const BDeskbar::GetItemInfo(const char *name, int32 *_id) const
{ {
if (name == NULL) { if (name == NULL)
return(B_BAD_VALUE); 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;
} }
BMessage requestMessage(gGetItemInfoWhat);
BMessage replyMessage;
status_t result;
result = requestMessage.AddString("name", name); bool
if (result == B_OK) { BDeskbar::HasItem(int32 id) const
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 request(kMsgHasItem);
BMessage replyMessage; request.AddInt32("id", id);
bool result = false;
if ((requestMessage.AddInt32("id", id) == B_OK) && BMessage reply;
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK)) { if (fMessenger->SendMessage(&request, &reply) == B_OK)
replyMessage.FindBool("exists", &result); return reply.FindBool("exists");
return false;
} }
return(result);
}
//------------------------------------------------------------------------------ bool
bool BDeskbar::HasItem(const char *name) const BDeskbar::HasItem(const char *name) const
{ {
BMessage requestMessage(gHasItemWhat); BMessage request(kMsgHasItem);
BMessage replyMessage; request.AddString("name", name);
bool result = false;
if ((requestMessage.AddString("name", name) == B_OK) && BMessage reply;
(fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK)) { if (fMessenger->SendMessage(&request, &reply) == B_OK)
replyMessage.FindBool("exists", &result); return reply.FindBool("exists");
return false;
} }
return(result);
}
//------------------------------------------------------------------------------ uint32
uint32 BDeskbar::CountItems(void) const BDeskbar::CountItems(void) const
{ {
BMessage requestMessage(gCountItemsWhat); BMessage request(kMsgCountItems);
BMessage replyMessage; BMessage reply;
int32 result = 0;
if ((fMessenger->SendMessage(&requestMessage, &replyMessage) == B_OK) && if (fMessenger->SendMessage(&request, &reply) == B_OK)
(replyMessage.what == 'rply')) { return reply.FindInt32("count");
replyMessage.FindInt32("count", &result);
return 0;
} }
return(static_cast<uint32>(result));
}
//------------------------------------------------------------------------------ status_t
status_t BDeskbar::AddItem(BView *archivableView, int32 *id) BDeskbar::AddItem(BView *view, int32 *_id)
{ {
BMessage requestMessage(gAddItemViewWhat); BMessage archive;
BMessage viewMessage; status_t result = view->Archive(&archive);
BMessage replyMessage; if (result < B_OK)
status_t result; return result;
result = archivableView->Archive(&viewMessage); BMessage request(kMsgAddView);
request.AddMessage("view", &archive);
BMessage reply;
result = fMessenger->SendMessage(&request, &reply);
if (result == B_OK) { if (result == B_OK) {
result = requestMessage.AddMessage("view", &viewMessage); if (_id != NULL)
if (result == B_OK) { result = reply.FindInt32("id", _id);
result = fMessenger->SendMessage(&requestMessage, &replyMessage); else
if ((result == B_OK) && reply.FindInt32("error", &result);
(id != NULL)) {
result = replyMessage.FindInt32("id", id);
} }
return result;
} }
}
return(result);
} status_t
//------------------------------------------------------------------------------ BDeskbar::AddItem(entry_ref *addon, int32 *_id)
status_t BDeskbar::AddItem(entry_ref *addon, int32 *id)
{ {
BMessage requestMessage(gAddItemViewWhat); BMessage request(kMsgAddAddOn);
BMessage replyMessage; request.AddRef("addon", addon);
status_t result;
result = requestMessage.AddRef("addon", addon); // Note: to make Deskbar items persistent, they need to have the attribute
if (result == B_OK) { // set. The Deskbar will remove the attribute automatically when needed.
result = fMessenger->SendMessage(&requestMessage, &replyMessage); // ToDo: move this functionality into the Deskbar itself!
if ((result == B_OK) &&
(id != NULL)) {
result = replyMessage.FindInt32("id", id);
/* NOTE: I add this because the persistent state of the item BNode node;
is not set by the Deskbar itself and it needs to be done somewhere. status_t status = node.SetTo(addon);
In fact, telling the Deskbar about the addon ref is not enough and if (status < B_OK)
adding this attribute is mandatory. return status;
!! Deskbar seems to remove the attribute in RemoveItem though !!
*/ if ((status = node.WriteAttr("be:deskbar_item_status", B_STRING_TYPE,
if (result == B_OK) { 0, "enabled", strlen("enabled"))) < B_OK)
BNode node(addon); return status;
node.WriteAttr("be:deskbar_item_status",
B_STRING_TYPE, 0, "enabled", strlen("enabled")); 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;
} }
}
return(result);
} status_t
//------------------------------------------------------------------------------ BDeskbar::RemoveItem(int32 id)
status_t BDeskbar::RemoveItem(int32 id)
{ {
BMessage requestMessage(gRemoveItemWhat); BMessage request(kMsgRemoveItem);
BMessage replyMessage; request.AddInt32("id", id);
status_t result;
result = requestMessage.AddInt32("id", id); // ToDo: the Deskbar does not reply to this message, so we don't
if (result == B_OK) { // know if it really succeeded - we can just acknowledge that the
result = fMessenger->SendMessage(&requestMessage, &replyMessage); // message was sent to the Deskbar
return fMessenger->SendMessage(&request);
} }
/* 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. status_t
*/ BDeskbar::RemoveItem(const char *name)
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 request(kMsgRemoveItem);
BMessage replyMessage; request.AddString("name", name);
status_t result;
result = requestMessage.AddString("name", name); // ToDo: the Deskbar does not reply to this message, so we don't
if (result == B_OK) { // know if it really succeeded - we can just acknowledge that the
result = fMessenger->SendMessage(&requestMessage, &replyMessage); // message was sent to the Deskbar
}
/* same as above */ return fMessenger->SendMessage(&request);
if (result != B_OK)
return(result);
if (replyMessage.what == B_NO_REPLY)
return(B_OK);
result = B_ERROR;
replyMessage.FindInt32("error", &result);
return(result);
} }
//------------------------------------------------------------------------------