haiku/src/kits/tracker/FavoritesConfig.cpp
Jonas Sundström 019f90e634 Setting the escape key as shortcut for the Cancel options of Tracker's dialogs.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29958 a95241bf-73f2-0310-859d-f6bbb57e9c96
2009-04-05 23:47:58 +00:00

2302 lines
60 KiB
C++

/*
Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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 applies to all licensees
and 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 TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED 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.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
// ***************************************************************************************
// BeMenu configuration
//
// add temp item on dnd with new group
// save window location
// selection information
// last group showing
// recents
// auto scroll of contentsmenu
// default settings
// off screen drawing
// return/cancel in editingtext for name invokes name change
// after New Folder, scroll to selection
// move of items to folders
// change parent folder btn to be a NavMenu
//
// ***************************************************************************************
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Alert.h>
#include <Application.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <CheckBox.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <FilePanel.h>
#include <MenuItem.h>
#include <MenuField.h>
#include <MessageRunner.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Screen.h>
#include <SymLink.h>
#include <TextControl.h>
#include <TextView.h>
#include <Volume.h>
// Tracker
#include "Commands.h"
#include "IconCache.h"
#include "IconMenuItem.h"
#include "Model.h"
#include "ObjectList.h"
#include "tracker_private.h"
#include "Utilities.h"
#include "FavoritesConfig.h"
enum {
kRemove = 'rmve',
kAdd,
kRecentDocs,
kRecentFolders,
kRecentApps,
kNewGroup,
kItemSelected,
kOpenItem,
kEditItem,
kDoubleClick,
kRecentDocsCount,
kRecentFoldersCount,
kRecentAppsCount,
kScrollUp,
kScrollDown,
kShowGroup,
kTraverseUp,
kNameChange
};
const rgb_color kAlmostWhite = {232, 232, 232, 255};
const rgb_color kLightGray = {136, 136, 136, 255};
const rgb_color kMediumGray = {96, 96, 96, 255};
const float kMinBtnWidth = 75.0f;
const float kMenuFieldPad = 30.0f;
const float kMenuFieldGap = 5.0f;
const float kCheckBoxPad = 20.0f;
const float kTextControlPad = 15.0f;
const char *const kOpenStr = "Open";
const char *const kRemoveStr = "Remove";
const char *const kShowGroupStr = "Show Group";
const char *const kSortByStr = "Sort By";
const char *const kNewGroupStr = "New Group";
const char *const kRecentDocsStr = "Recent Documents";
const char *const kRecentFoldersStr = "Recent Folders";
const char *const kRecentAppsStr = "Recent Applications";
const char *const kShowStr = "Show:";
const unsigned char kLargeNewGroupIcon [] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfa, 0xfa, 0x00, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfa, 0xf8, 0xfa, 0xfa, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfa, 0xf8, 0xf8, 0xf8, 0xfa, 0xfa, 0x00,
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfa, 0xfa, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xfa,
0xfa, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfa, 0xf8, 0xfa, 0xfa, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8,
0xf8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfa, 0xf8, 0xf8, 0xf8, 0xfa, 0xfa, 0x00, 0x00, 0xf8, 0xf8,
0xf8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfa, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xfa, 0xfa, 0x00, 0xf8,
0xf8, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x0e,
0x0f, 0x1c, 0x1c, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x0f, 0x0f, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x3f,
0x3f, 0x0e, 0x0f, 0x1c, 0x1c, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x0e, 0x0f, 0xf8, 0xf8, 0x00, 0x00,
0x3f, 0x3f, 0x3f, 0x0e, 0x0f, 0x1c, 0x1c, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x3f, 0x3f, 0x0e, 0x0f, 0x1c, 0x1c,
0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x0e, 0x0f, 0x1a, 0x19, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x0e, 0x0f,
0x1c, 0x1c, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x0e, 0x1a, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x0e, 0x0f, 0x1c, 0x1c, 0x00, 0x00, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x0e, 0x0f, 0x1a, 0x19, 0x00, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x0e, 0x1a, 0x19, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x0f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x18, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1b, 0x1c, 0x17, 0x18, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1b, 0x1c, 0x17, 0x18, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x19, 0x1a, 0x17, 0x17, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x17, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x19, 0x1a, 0x17, 0x17,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x17, 0x1a, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x19, 0x1a,
0x17, 0x17, 0x3f, 0x3f, 0x3f, 0x17, 0x19, 0x00, 0x1a, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x19, 0x1a, 0x17, 0x17, 0x3f, 0x17, 0x19, 0x00, 0x00, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x19, 0x1a, 0x17, 0x17, 0x1a, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x19, 0x1a, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x19, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
const unsigned char kSmallNewGroupIcon [] = {
0xff, 0xff, 0xff, 0xff, 0x0e, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x0e, 0xfa, 0xfa, 0x0e, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0x0e, 0xf8, 0xf8, 0xfa, 0xfa, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0xfa, 0xfa, 0x0e, 0xf8, 0xf8, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0xf8, 0xf8, 0xfa, 0xfa, 0x0e, 0x0e, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0x18, 0x18, 0xf8, 0xf8, 0x0e, 0x3f, 0x3f, 0x0e, 0x0e, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0x18, 0x3f, 0x18, 0x18, 0x0f, 0x0f, 0x3f, 0x3f, 0x0e, 0x12, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0x18, 0x3f, 0x3f, 0x3f, 0x18, 0x18, 0x0f, 0x0f, 0x0e, 0x12, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0x18, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x18, 0x18, 0x0e, 0x12, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0x18, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x1c, 0x0e, 0x12, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0e, 0x0e, 0x18, 0x1c, 0x3f, 0x3f, 0x3f, 0x3f, 0x1c, 0x0e, 0x12, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x0e, 0x0e, 0x17, 0x1c, 0x3f, 0x3f, 0x1c, 0x0e, 0x12, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x0e, 0x17, 0x1c, 0x1c, 0x0e, 0x12, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x0e, 0x17, 0x0e, 0x12, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x0e, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
void
CenterWindowOnScreen(BWindow *window)
{
BRect screenFrame = BScreen(B_MAIN_SCREEN_ID).Frame();
BPoint point;
point.x = screenFrame.Width() / 2 - window->Bounds().Width() / 2;
point.y = screenFrame.Height() / 2 - window->Bounds().Height() / 2;
if (screenFrame.Contains(point))
window->MoveTo(point);
}
float
FontHeight(const BFont *font, bool full)
{
font_height finfo;
font->GetHeight(&finfo);
float height = finfo.ascent + finfo.descent;
if (full)
height += finfo.leading;
return height;
}
// #pragma mark -
TFavoritesConfigWindow::TFavoritesConfigWindow(BRect frame, const char *title,
bool modal, uint32 filePanelNodeFlavors, BMessenger parent, const entry_ref *startRef,
int32 maxApps, int32 maxDocs, int32 maxFolders)
: BWindow(frame, title, B_TITLED_WINDOW_LOOK,
modal ? B_MODAL_APP_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL,
B_NOT_ZOOMABLE | B_NOT_RESIZABLE),
fFilePanelNodeFlavors(filePanelNodeFlavors),
fParent(parent),
fCurrentRef(*startRef),
fAddPanel(NULL)
{
Lock();
// show the window offscreen
// so that the placement calculations actually work
MoveTo(-1024, -1024);
Show();
AddParts(maxApps, maxDocs, maxFolders);
CenterWindowOnScreen(this);
Unlock();
AddShortcut('R', B_COMMAND_KEY, new BMessage(kRemove));
AddShortcut('A', B_COMMAND_KEY, new BMessage(kAdd));
AddShortcut('E', B_COMMAND_KEY, new BMessage(kEditItem));
AddShortcut('O', B_COMMAND_KEY, new BMessage(kOpenItem));
AddShortcut('N', B_COMMAND_KEY, new BMessage(kNewGroup));
AddShortcut(B_UP_ARROW, B_COMMAND_KEY, new BMessage(kTraverseUp));
BMessenger tracker(kTrackerSignature);
StartWatching(tracker, kFavoriteCountChanged);
}
TFavoritesConfigWindow::~TFavoritesConfigWindow()
{
// node monitoring the be menu directory
stop_watching(this);
if (fAddPanel && fAddPanel->IsShowing())
// kill the filepanel if its still showing
fAddPanel->Hide();
BMessenger tracker(kTrackerSignature);
StopWatching(tracker, kFavoriteCountChanged);
}
void
TFavoritesConfigWindow::MessageReceived(BMessage *message)
{
switch (message->what) {
// from menuthing selection
case kItemSelected:
UpdateButtons();
break;
// double click on item in menuthing
case kDoubleClick:
{
entry_ref ref;
if (message->FindRef("current", &ref) == B_OK)
OpenGroup(&ref);
}
break;
// open button
case kOpenItem:
if (fMenuThing->Value() >= 0 && fMenuThing->Value() < fMenuThing->ItemCount())
OpenGroup(fMenuThing->ItemAt(fMenuThing->Value())->EntryRef());
break;
case kEditItem:
{
int32 selection = fMenuThing->Value();
if (selection < 0)
break;
const Model *item = fMenuThing->ItemAt(selection);
if (!item)
break;
new NameItemPanel(this, item->Name());
// shows itself, kills itself
}
break;
case 'canc':
break;
case kNameChange:
{
const char *name;
if (message->FindString("name", &name) == B_OK) {
int32 selection = fMenuThing->Value();
if (selection < 0)
break;
const Model *item = fMenuThing->ItemAt(selection);
if (!item)
break;
const entry_ref *ref = item->EntryRef();
if (strcmp(ref->name, name) != 0) {
BEntry entry(ref);
if (entry.InitCheck() == B_OK && entry.Exists())
entry.Rename(name);
}
}
}
break;
// change of selection in directory menu
case kShowGroup:
{
entry_ref ref;
if (message->FindRef("current", &ref) == B_OK)
ShowGroup(&ref);
}
break;
// alt-up arrow
case kTraverseUp:
{
BMenuItem *item = fGroupMenu->FindMarked();
if (item) {
int32 index = fGroupMenu->IndexOf(item) - 1;
if (index < 0)
break;
item = fGroupMenu->ItemAt(index);
if (item) {
BMessage *message = item->Message();
if (message) {
entry_ref ref;
if (message->FindRef("current", &ref) == B_OK) {
ShowGroup(&ref);
}
}
}
}
}
break;
// new group icon
case kNewGroup:
AddNewGroup();
break;
// Add button
case kAdd:
PromptForAdd();
break;
// from Add btn - FilePanel
case B_REFS_RECEIVED:
AddRefs(message);
// fall through
case B_CANCEL:
fAddPanel = NULL;
break;
// remove btn
case kRemove:
fMenuThing->RemoveItem(fMenuThing->Value());
break;
// recents items
case kRecentFolders:
fRecentFoldersFld->SetEnabled(fRecentFoldersBtn->Value() != 0);
if (fRecentFoldersBtn->Value())
UpdateFoldersCount();
else
UpdateFoldersCount(0);
break;
case kRecentDocs:
fRecentDocsFld->SetEnabled(fRecentDocsBtn->Value() != 0);
if (fRecentDocsBtn->Value())
UpdateDocsCount();
else
UpdateDocsCount(0);
break;
case kRecentApps:
fRecentAppsFld->SetEnabled(fRecentAppsBtn->Value() != 0);
if (fRecentAppsBtn->Value())
UpdateAppsCount();
else
UpdateAppsCount(0);
break;
case kRecentFoldersCount:
UpdateFoldersCount();
break;
case kRecentDocsCount:
UpdateDocsCount();
break;
case kRecentAppsCount:
UpdateAppsCount();
break;
case B_OBSERVER_NOTICE_CHANGE:
{
int32 observerWhat;
if (message->FindInt32("be:observe_change_what", &observerWhat) == B_OK) {
switch (observerWhat) {
case kFavoriteCountChanged:
{
int32 appCount = 10;
int32 documentCount = 10;
int32 folderCount = 10;
if (message->FindInt32("RecentApplications", &appCount) == B_OK) {
BString appStr;
appStr << appCount;
fRecentAppsFld->SetText(appStr.String());
UpdateAppsCount(appCount, false);
// false -> do not tell tracker
}
if (message->FindInt32("RecentDocuments", &documentCount) == B_OK) {
BString docStr;
docStr << documentCount;
fRecentDocsFld->SetText(docStr.String());
UpdateDocsCount(documentCount, false);
// false -> do not tell tracker
}
if (message->FindInt32("RecentFolders", &folderCount) == B_OK) {
BString folderStr;
folderStr << folderCount;
fRecentFoldersFld->SetText(folderStr.String());
UpdateFoldersCount(folderCount, false);
// false -> do not tell tracker
}
}
break;
}
}
}
break;
default:
BWindow::MessageReceived(message);
break;
}
}
bool
TFavoritesConfigWindow::QuitRequested()
{
// tell the app the config panel is closing
BMessage message(kConfigClose);
int32 count;
if (fRecentAppsFld) {
if (fRecentAppsFld->IsEnabled())
count = atoi(fRecentAppsFld->Text());
else
count = 0;
message.AddInt32("applications", count);
}
if (fRecentFoldersFld) {
if (fRecentFoldersFld->IsEnabled())
count = atoi(fRecentFoldersFld->Text());
else
count = 0;
message.AddInt32("folders", count);
}
if (fRecentDocsFld) {
if (fRecentDocsFld->IsEnabled())
count = atoi(fRecentDocsFld->Text());
else
count = 0;
message.AddInt32("documents", count);
}
fParent.SendMessage(&message);
return true;
}
void
TFavoritesConfigWindow::AddParts(int32 maxApps, int32 maxDocs, int32 maxFolders)
{
// build the interface
AddBeMenuPane(maxApps, maxDocs, maxFolders);
// find the bg with the largest dimensions
ResizeTo(fBeMenuPaneBG->Frame().Width(), fBeMenuPaneBG->Frame().Height());
// enable node watching
// fill the pseudo menu
OpenGroup(&fCurrentRef);
}
void
TFavoritesConfigWindow::BuildCommon(BRect *frame, int32 count, const char *string,
uint32 btnWhat, uint32 fldWhat, BCheckBox **button, BTextControl **field, BBox *parent)
{
frame->right = frame->left + be_plain_font->StringWidth(string)
+ kCheckBoxPad;
BCheckBox *newButton = new BCheckBox(*frame, "recents btn", string,
new BMessage(btnWhat), B_FOLLOW_BOTTOM | B_FOLLOW_LEFT,
B_WILL_DRAW | B_NAVIGABLE);
parent->AddChild(newButton);
newButton->SetValue(count > 0);
float width = be_plain_font->StringWidth(kShowStr);
frame->right = frame->left + width
+ (be_plain_font->StringWidth("0") * 4) + kTextControlPad;
BTextControl *newFld = new BTextControl(*frame, "recents fld", kShowStr, "",
new BMessage(fldWhat), B_FOLLOW_BOTTOM | B_FOLLOW_LEFT,
B_WILL_DRAW | B_NAVIGABLE);
parent->AddChild(newFld);
newFld->SetDivider(width + 5);
newFld->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_CENTER);
newFld->SetEnabled(count>0);
char str[32];
sprintf(str, "%ld", count);
newFld->SetText(str);
BTextView *textView = newFld->TextView();
textView->SetMaxBytes(2);
for (uint32 index = 0; index < 256; index++)
textView->DisallowChar(index);
for (uint32 index = '0'; index <= '9'; index++)
textView->AllowChar(index);
textView->AllowChar(B_BACKSPACE);
*button = newButton;
*field = newFld;
}
void
TFavoritesConfigWindow::AddBeMenuPane(int32 maxApps, int32 maxDocs, int32 maxFolders)
{
fBeMenuPaneBG = new BBox(Bounds(), "bg", B_FOLLOW_NONE,
B_WILL_DRAW, B_PLAIN_BORDER);
AddChild(fBeMenuPaneBG);
BRect frame;
float width = 0;
// New Group
fNewGroupBtn = new TDraggableIconButton(BRect(10, 0, 41, 31), kNewGroupStr,
new BMessage(kNewGroup), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM,
B_WILL_DRAW | B_NAVIGABLE);
fBeMenuPaneBG->AddChild(fNewGroupBtn);
// Recent Documents
frame.left = 10;
if (maxDocs > -1) {
BuildCommon(&frame, maxDocs, kRecentDocsStr,
kRecentDocs, kRecentDocsCount, &fRecentDocsBtn, &fRecentDocsFld,
fBeMenuPaneBG);
} else {
fRecentDocsBtn = NULL;
fRecentDocsFld = NULL;
}
// Recent Applications
if (maxApps > -1) {
BuildCommon(&frame, maxApps, kRecentAppsStr,
kRecentApps, kRecentAppsCount, &fRecentAppsBtn, &fRecentAppsFld,
fBeMenuPaneBG);
} else {
fRecentAppsBtn = NULL;
fRecentAppsFld = NULL;
}
// Recent Folders
if (maxFolders > -1) {
BuildCommon(&frame, maxFolders, kRecentFoldersStr,
kRecentFolders, kRecentFoldersCount, &fRecentFoldersBtn, &fRecentFoldersFld,
fBeMenuPaneBG);
} else {
fRecentFoldersBtn = NULL;
fRecentFoldersFld = NULL;
}
// will place this items left edge relative to the contents list
fGroupMenu = new BPopUpMenu("Show Group");
width = be_plain_font->StringWidth("Some Long Name For a Group")
+ kMenuFieldPad;
frame.Set(0, 10, width, 11);
fGroupBtn = new BMenuField(frame, "group btn", kShowGroupStr, fGroupMenu);
fBeMenuPaneBG->AddChild(fGroupBtn);
fGroupBtn->HidePopUpMarker();
fGroupBtn->SetDivider(0);
#ifdef IS_SORTABLE
// not sortable now, ever (?)
fSortMenu = new BPopUpMenu("Sort By");
fSortMenu->AddItem(new BMenuItem("Sort One", NULL));
fSortMenu->AddItem(new BMenuItem("Sort Two", NULL));
frame.OffsetBy(0, fGroupBtn->Frame().Height() + kMenuFieldGap);
fSortBtn = new BMenuField(frame, "sort btn", kSortByStr, fSortMenu);
fBeMenuPaneBG->AddChild(fSortBtn);
fSortBtn->SetDivider(be_plain_font->StringWidth(kSortByStr) + 5);
#endif
// Contents List
// placement is relative to controls in place
// may or may not have recents items
// always will have New Group button
#ifdef IS_SORTABLE
frame.top = fSortBtn->Frame().bottom + 10;
#else
frame.top = fGroupBtn->Frame().bottom + 10;
#endif
frame.bottom = frame.top + 1;
if (maxApps > -1 && fRecentAppsBtn)
frame.left = fRecentAppsBtn->Frame().right + 10;
else if (maxDocs > -1 && fRecentDocsBtn)
frame.left = fRecentDocsBtn->Frame().right + 10;
else if (maxFolders > -1 && fRecentFoldersBtn)
frame.left = fRecentFoldersBtn->Frame().right + 10;
else
frame.left = fGroupBtn->Frame().right + 10;
frame.right = frame.left + 1;
fMenuThing = new TContentsMenu(frame, new BMessage(kItemSelected),
new BMessage(kDoubleClick), 10, &fCurrentRef);
fBeMenuPaneBG->AddChild(fMenuThing);
width = be_plain_font->StringWidth("Edit"B_UTF8_ELLIPSIS);
frame.Set(0, 0, (kMinBtnWidth >= width) ? kMinBtnWidth : width, 1);
fEditBtn = new BButton(frame, "edit", "Edit"B_UTF8_ELLIPSIS, new BMessage(kEditItem),
B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE);
fBeMenuPaneBG->AddChild(fEditBtn);
width = be_plain_font->StringWidth(kOpenStr);
frame.Set(0, 0, (kMinBtnWidth >= width) ? kMinBtnWidth : width, 1);
fOpenBtn = new BButton(frame, "open", kOpenStr, new BMessage(kOpenItem),
B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE);
fBeMenuPaneBG->AddChild(fOpenBtn);
width = be_plain_font->StringWidth("Add"B_UTF8_ELLIPSIS);
frame.Set(0, 0, (kMinBtnWidth >= width) ? kMinBtnWidth : width, 1);
fAddBtn = new BButton(frame, "add", "Add"B_UTF8_ELLIPSIS, new BMessage(kAdd),
B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE);
fBeMenuPaneBG->AddChild(fAddBtn);
width = be_plain_font->StringWidth(kRemoveStr);
frame.Set(0, 0, (kMinBtnWidth >= width) ? kMinBtnWidth : width, 1);
fRemoveBtn = new BButton(frame, "remove", kRemoveStr, new BMessage(kRemove),
B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE);
fBeMenuPaneBG->AddChild(fRemoveBtn);
// initially disable buttons
fRemoveBtn->SetEnabled(false);
fOpenBtn->SetEnabled(false);
fEditBtn->SetEnabled(false);
// contents list will auto resize
// resizing of parent pane will cause
// contents list and buttons to flow
fOpenBtn->MoveTo(fMenuThing->Frame().right - fOpenBtn->Frame().Width(),
fMenuThing->Frame().bottom + 10);
fEditBtn->MoveTo(fOpenBtn->Frame().left - 10 - fEditBtn->Frame().Width(),
fOpenBtn->Frame().top);
fRemoveBtn->MoveTo(fOpenBtn->Frame().left, fOpenBtn->Frame().bottom + 5);
fAddBtn->MoveTo(fEditBtn->Frame().left, fRemoveBtn->Frame().top);
// resize the pane so that everything is visible
fBeMenuPaneBG->ResizeTo(fMenuThing->Frame().right + 10,
fMenuThing->Frame().bottom + 10 + fOpenBtn->Frame().Height() + 5
+ fRemoveBtn->Frame().Height() + 10);
// place the remaining controls relative to the menu thing
float bottom;
float left = 10;
BRect menuthingframe = fMenuThing->Frame();
if (maxFolders > -1 && fRecentFoldersFld && fRecentFoldersBtn) {
bottom = menuthingframe.bottom;
fRecentFoldersFld->MoveTo(
menuthingframe.left - fRecentFoldersFld->Frame().Width() - 10,
bottom - fRecentFoldersFld->Frame().Height());
fRecentFoldersBtn->MoveTo(10,
fRecentFoldersFld->Frame().top - fRecentFoldersBtn->Frame().Height());
left = fRecentFoldersBtn->Frame().right
- (fRecentFoldersBtn->Frame().Width() / 2) - (fNewGroupBtn->Frame().Width() / 2);
}
if (maxApps > -1 && fRecentAppsFld && fRecentAppsBtn) {
if (maxFolders > -1 && fRecentFoldersFld && fRecentFoldersBtn)
bottom = fRecentFoldersBtn->Frame().top - 20;
else {
bottom = menuthingframe.bottom;
left = fRecentAppsBtn->Frame().right
- (fRecentAppsBtn->Frame().Width() / 2) - (fNewGroupBtn->Frame().Width() / 2);
}
fRecentAppsFld->MoveTo(
menuthingframe.left - fRecentAppsFld->Frame().Width() - 10,
bottom - fRecentAppsFld->Frame().Height());
fRecentAppsBtn->MoveTo(10,
fRecentAppsFld->Frame().top - fRecentAppsBtn->Frame().Height());
}
if (maxDocs > -1 && fRecentDocsFld && fRecentDocsBtn) {
if (maxApps > -1 && fRecentAppsFld && fRecentAppsBtn)
bottom = fRecentAppsBtn->Frame().top - 20;
else if (maxFolders > -1 && fRecentFoldersFld && fRecentFoldersBtn)
bottom = fRecentFoldersBtn->Frame().top - 20;
else {
bottom = menuthingframe.bottom;
left = fRecentDocsBtn->Frame().right
- (fRecentDocsBtn->Frame().Width() / 2) - (fNewGroupBtn->Frame().Width() / 2);
}
fRecentDocsFld->MoveTo(
menuthingframe.left - fRecentDocsFld->Frame().Width() - 10,
bottom - fRecentDocsFld->Frame().Height());
fRecentDocsBtn->MoveTo(10,
fRecentDocsFld->Frame().top - fRecentDocsBtn->Frame().Height());
}
// place this button relative (centered) to
// any recents items and content list
fNewGroupBtn->MoveTo(left, menuthingframe.top);
#ifdef IS_SORTABLE
fSortBtn->MoveTo(menuthingframe.left - fSortBtn->Divider(),
fSortBtn->Frame().top);
#endif
fGroupBtn->MoveTo(menuthingframe.left - fGroupBtn->Divider(),
fGroupBtn->Frame().top);
}
static void
GetNextGroupName(BDirectory *dir, char *directoryName)
{
int32 index = 1;
for (;;) {
sprintf(directoryName, "Untitled Group %li", index);
if (!dir->Contains(directoryName))
break;
else
index++;
}
}
void
TFavoritesConfigWindow::AddNewGroup(entry_ref *dirRef, entry_ref *newGroup)
{
BDirectory dir(dirRef);
char directoryName[B_FILE_NAME_LENGTH];
GetNextGroupName(&dir, directoryName);
BDirectory subdir;
dir.CreateDirectory(directoryName, &subdir);
BEntry entry;
subdir.GetEntry(&entry);
entry.GetRef(newGroup);
}
void
TFavoritesConfigWindow::AddSymLink(const entry_ref *dirRef, const entry_ref *target)
{
BDirectory dir(dirRef);
BEntry entry(target);
BPath path;
entry.GetPath(&path);
BSymLink symlink;
dir.CreateSymLink(target->name, path.Path(), &symlink);
}
void
TFavoritesConfigWindow::AddNewGroup()
{
// from New Group/Folder button
// add a new group/folder to this directory
entry_ref newGroup;
AddNewGroup(&fCurrentRef, &newGroup);
}
void
TFavoritesConfigWindow::PromptForAdd()
{
if (fAddPanel)
fAddPanel->Show();
// does an activate on the window
else {
BMessenger messenger(this, this);
// determine a starting point for where apps are added from
char appPath[B_PATH_NAME_LENGTH];
// search for an apps directory
if (find_directory(B_APPS_DIRECTORY, 0, false, appPath, B_PATH_NAME_LENGTH) == B_OK) {
entry_ref ref;
// get reference to application directory
get_ref_for_path(appPath, &ref);
fAddPanel = new BFilePanel(B_OPEN_PANEL, &messenger,
&ref, fFilePanelNodeFlavors, true);
} else
fAddPanel = new BFilePanel(B_OPEN_PANEL, &messenger,
NULL, fFilePanelNodeFlavors, true);
fAddPanel->SetButtonLabel(B_DEFAULT_BUTTON, "Add");
fAddPanel->Show();
}
}
void
TFavoritesConfigWindow::AddRefs(BMessage *message)
{
// add any refs in the message to the current directory
int32 index = 0;
entry_ref ref;
while (message->FindRef("refs", index, &ref) == B_OK) {
AddSymLink(&fCurrentRef, &ref);
index++;
}
}
// open a new ref to show, tunnelling down
void
TFavoritesConfigWindow::OpenGroup(const entry_ref *ref)
{
if (!ref)
return;
fCurrentRef = *ref;
fMenuThing->SetStartRef(&fCurrentRef);
fMenuThing->SetValue(0);
BMessage *message = new BMessage(kShowGroup);
message->AddRef("current", &fCurrentRef);
ModelMenuItem *item = new ModelMenuItem(new Model(&fCurrentRef), fCurrentRef.name, message);
fGroupMenu->AddItem(item);
item->SetMarked(true);
UpdateButtons();
}
// specify a new ref to show, tunnelling up
void
TFavoritesConfigWindow::ShowGroup(const entry_ref *groupRef)
{
if (!groupRef)
return;
entry_ref lastRef = fCurrentRef;
fCurrentRef = *groupRef;
fMenuThing->SetStartRef(&fCurrentRef);
fMenuThing->Select(&lastRef);
// find the item that was selected
int32 count = fGroupMenu->CountItems() - 1;
for (int32 index = count ; index >= 0 ; index--) {
BMenuItem *item = fGroupMenu->ItemAt(index);
if (item) {
BMessage *message = item->Message();
if (message) {
entry_ref ref;
if (message->FindRef("current", &ref) == B_OK) {
// if we have a match
// delete all the items below this item
if (ref == fCurrentRef) {
for (int32 j = count ; j > index ; j--)
delete fGroupMenu->RemoveItem(j);
item->SetMarked(true);
break;
}
}
}
}
}
UpdateButtons();
}
void
TFavoritesConfigWindow::UpdateButtons()
{
// Open is enabled only if this item is an actual directory
// Edit is enabled for any selection
// Remove is selected only if there is a selection
// Add is always enabled
int32 selection = fMenuThing->Value();
if (selection >= 0 && selection < fMenuThing->ItemCount())
fOpenBtn->SetEnabled(fMenuThing->ItemAt(selection)->IsDirectory());
else
fOpenBtn->SetEnabled(false);
fEditBtn->SetEnabled(selection >= 0);
fRemoveBtn->SetEnabled(selection >= 0);
}
void
TFavoritesConfigWindow::UpdateFoldersCount(int32 count, bool notifyTracker)
{
if (count == -1)
count = atoi(fRecentFoldersFld->Text());
BMessage message(kUpdateFolderCount);
message.AddInt32("count", count);
fParent.SendMessage(&message);
if (notifyTracker) {
BMessenger tracker(kTrackerSignature);
BMessage notificationMessage(kFavoriteCountChangedExternally);
notificationMessage.AddInt32("RecentFolders", count);
tracker.SendMessage(&notificationMessage);
}
}
void
TFavoritesConfigWindow::UpdateDocsCount(int32 count, bool notifyTracker)
{
if (count == -1)
count = atoi(fRecentDocsFld->Text());
BMessage message(kUpdateDocsCount);
message.AddInt32("count", count);
fParent.SendMessage(&message);
if (notifyTracker) {
BMessenger tracker(kTrackerSignature);
BMessage notificationMessage(kFavoriteCountChangedExternally);
notificationMessage.AddInt32("RecentDocuments", count);
tracker.SendMessage(&notificationMessage);
}
}
void
TFavoritesConfigWindow::UpdateAppsCount(int32 count, bool notifyTracker)
{
if (count == -1)
count = atoi(fRecentAppsFld->Text());
BMessage message(kUpdateAppsCount);
message.AddInt32("count", count);
fParent.SendMessage(&message);
if (notifyTracker) {
BMessenger tracker(kTrackerSignature);
BMessage notificationMessage(kFavoriteCountChangedExternally);
notificationMessage.AddInt32("RecentApplications", count);
tracker.SendMessage(&notificationMessage);
}
}
// ***************************************************************************************
TDraggableIconButton::TDraggableIconButton(BRect frame, const char *label,
BMessage *message, uint32 resizeMask, uint32 flags)
: BControl(frame, "draggable icon", label, message, resizeMask, flags),
fIcon(NULL)
{
}
TDraggableIconButton::~TDraggableIconButton()
{
}
void
TDraggableIconButton::AttachedToWindow()
{
BControl::AttachedToWindow();
if (Parent())
SetViewColor(Parent()->ViewColor());
else
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
fIcon = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
fIcon->SetBits(kLargeNewGroupIcon, fIcon->BitsLength(), 0, B_CMAP8);
// calculate correct frame for icon and label
// sets icon rect and label rect for drawing
ResizeToPreferred();
}
void
TDraggableIconButton::DetachedFromWindow()
{
BControl::DetachedFromWindow();
delete fIcon;
fIcon = NULL;
}
void
TDraggableIconButton::Draw(BRect)
{
PushState();
SetHighColor(kBlack);
SetLowColor(ViewColor());
if (fIcon) {
SetDrawingMode(B_OP_OVER);
DrawBitmapAsync(fIcon, fIconRect);
}
SetDrawingMode(B_OP_COPY);
MovePenTo(fLabelRect.LeftBottom());
DrawString(Label());
if (IsEnabled() && IsFocus() && Window()->IsActive())
SetHighColor(kBlack);
else
SetHighColor(ViewColor());
StrokeRect(Bounds());
PopState();
}
void
TDraggableIconButton::MouseDown(BPoint where)
{
ulong buttons;
BPoint loc;
GetMouse(&loc, &buttons);
if (!buttons)
return;
fInitialClickRect.Set(where.x, where.y, where.x, where.y);
fInitialClickRect.InsetBy(-4, -4);
SetTracking(true);
fDragging = false;
SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
InvertRect(fIconRect);
}
void
TDraggableIconButton::MouseUp(BPoint where)
{
if (IsTracking()) {
InvertRect(fIconRect);
SetTracking(false);
fDragging = false;
if (Bounds().Contains(where) && fInitialClickRect.Contains(where))
// tell parent to add a new group
Invoke();
} else
BControl::MouseUp(where);
}
void
TDraggableIconButton::MouseMoved(BPoint where, uint32 code,
const BMessage *message)
{
if (IsTracking()) {
if (!fDragging && fIcon) {
DragMessage(new BMessage('icon'), new BBitmap(fIcon),
B_OP_BLEND, BPoint(15, 15));
fDragging = true;
fInitialClickRect.Set(0, 0, 0, 0);
}
} else
BControl::MouseMoved(where, code, message);
}
void
TDraggableIconButton::GetPreferredSize(float *width, float *height)
{
float stringWidth = be_plain_font->StringWidth(Label());
float fontHeight = FontHeight(be_plain_font, true);
*width = stringWidth + 10;
*height = 32 + fontHeight + 5;
fIconRect.Set((*width / 2) - 16, 2, (*width / 2) + 15, 33);
fLabelRect.Set((*width / 2) - (stringWidth / 2),
*height - fontHeight - 5, *width, *height - 5);
}
void
TDraggableIconButton::ResizeToPreferred()
{
float width, height;
GetPreferredSize(&width, &height);
ResizeTo(width, height);
}
const float kLeftGutter = 15.0f;
const float kHorizontalGap = 4.0f;
const float kItemGap = 4.0f;
const float kVerticalBorderSize = 2.0f;
const float kHorizontalBorderSize = 2.0f;
const int32 kMaxItemCount = 12;
const float kScrollerHeight = 16.0f;
TContentsMenu::TContentsMenu(BRect frame, BMessage *singleClick, BMessage *doubleClick,
int32 visibleItemCount, const entry_ref *startRef)
: BControl(frame, "contents menu", "contents menu label",
singleClick, B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE),
fDoubleClickMessage(doubleClick),
fVisibleItemCount(visibleItemCount),
fStartRef(*startRef),
fItemHeight(16),
#ifdef ITEM_EDIT
fEditingItem(false),
fEditingFld(NULL),
#endif
fFirstItem(0),
fContentsList(NULL),
fUpBtn(NULL),
fDownBtn(NULL)
{
}
TContentsMenu::~TContentsMenu()
{
delete fDoubleClickMessage;
}
void
TContentsMenu::AttachedToWindow()
{
BControl::AttachedToWindow();
menu_info minfo;
get_menu_info(&minfo);
// cache the menu font for drawing later on
#ifdef USE_MENU_FONT
// !! this should change when the menu font is used
fMenuFont = new BFont();
fMenuFont->SetFamilyAndStyle(minfo.f_family, minfo.f_style);
fMenuFont->SetSize(minfo.font_size);
#else
fMenuFont = new BFont(be_plain_font);
#endif
// cache the item height, greater of font height and icon height
fFontHeight = FontHeight(fMenuFont, true);
fItemHeight = (fFontHeight>fItemHeight) ? fFontHeight : fItemHeight;
fItemHeight += 3; // add a gutter
// mimicing a menu
SetViewColor(minfo.background_color);
#ifdef SNAKE
fHiliteColor = ui_color(B_MENU_SELECTION_BACKGROUND_COLOR);
#else
fHiliteColor = tint_color(minfo.background_color, B_DARKEN_2_TINT);
#endif
// add the buttons
// cache item frame, sub of frame - buttons
fContentsList = new BObjectList<Model>(kMaxItemCount);
SetValue(-1); // no item selected
ResizeToPreferred();
BRect frame(2, 2, Bounds().Width() - 2, 2 + kScrollerHeight);
fUpBtn = new TScrollerButton(frame, new BMessage(kScrollDown), true);
AddChild(fUpBtn);
fUpBtn->SetTarget(this, Window());
frame.bottom = Bounds().Height()-2;
frame.top = frame.bottom - kScrollerHeight;
fDownBtn = new TScrollerButton(frame, new BMessage(kScrollUp), false);
AddChild(fDownBtn);
fDownBtn->SetTarget(this, Window());
// cache the new group icon
fSmallGroupIcon = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
fSmallGroupIcon->SetBits(kSmallNewGroupIcon, fSmallGroupIcon->BitsLength(),
0, B_CMAP8);
// cache the symlink icon
BMimeType symlink("application/x-vnd.Be-symlink");
if (symlink.InitCheck() == B_OK) {
fSymlinkIcon = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
if (symlink.GetIcon(fSymlinkIcon, B_MINI_ICON) != B_OK)
fSymlinkIcon = NULL;
} else
fSymlinkIcon = NULL;
// set up node watch etc
SetStartRef(&fStartRef);
}
void
TContentsMenu::DetachedFromWindow()
{
EmptyMenu();
delete fContentsList;
fContentsList = NULL;
delete fMenuFont;
fMenuFont = NULL;
delete fSmallGroupIcon;
fSmallGroupIcon = NULL;
delete fSymlinkIcon;
fSymlinkIcon = NULL;
BControl::DetachedFromWindow();
}
const char *kUntitledItemStr = "<Untitled Item>";
void
TContentsMenu::Draw(BRect updateRect)
{
PushState();
// draw frame
SetLowColor(ViewColor());
BRect frame(Bounds());
// main border
#ifdef SNAKE
SetHighColor(kBlack);
StrokeRoundRect(frame, 3.0, 3.0);
#else
// exterior border
SetHighColor(100, 100, 100);
StrokeRect(frame);
// interior white
SetHighColor(233, 233, 233);
// top
StrokeLine(frame.LeftTop() + BPoint(1, 1), frame.RightTop() + BPoint(-1, 1));
// left
StrokeLine(frame.LeftTop() + BPoint(1, 1), frame.LeftBottom() + BPoint(1, -1));
// interior gray
SetHighColor(141, 141, 141);
// right
StrokeLine(frame.RightTop() + BPoint(-1, -1), frame.RightBottom() + BPoint(-1, -1));
// bottom
StrokeLine(frame.LeftBottom() + BPoint(1, -1), frame.RightBottom() + BPoint(-1, -1));
#endif
SetDrawingMode(B_OP_OVER);
BRect iconFrame, textFrame, itemFrame;
int32 max = fContentsList->CountItems() - fFirstItem;
int32 count = (max > kMaxItemCount) ? kMaxItemCount : max;
count += fFirstItem;
for (int32 index = fFirstItem ; index < count ; index++) {
Model *item = fContentsList->ItemAt(index);
if (!item)
continue;
if (!ItemFrame(index - fFirstItem, &iconFrame, &textFrame, &itemFrame))
continue;
if (!itemFrame.Intersects(updateRect))
continue;
// see if an item is selected
if (Value() == index) {
SetHighColor(fHiliteColor);
SetLowColor(fHiliteColor);
#ifdef SNAKE
FillRoundRect(itemFrame, 3.0, 3.0);
// main frame
SetHighColor(kBlack);
StrokeRoundRect(itemFrame, 3.0, 3.0);
#else
FillRect(itemFrame);
#endif
} else {
SetHighColor(ViewColor());
SetLowColor(ViewColor());
FillRect(itemFrame);
}
Model resolvedItem;
if (item->IsSymLink()) {
// if this item is a symlink
// see if it points to anything
// if it doesn't draw the broken symlink icon
BEntry entry(item->EntryRef(), true);
resolvedItem.SetTo(&entry);
if (entry.Exists()) {
// draw the real item icon
IconCache::sIconCache->Draw(&resolvedItem, this, iconFrame.LeftTop(),
kNormalIcon, B_MINI_ICON);
} else if (fSymlinkIcon){
SetDrawingMode(B_OP_OVER);
DrawBitmapAsync(fSymlinkIcon, iconFrame);
// this item doesn't exist
// get the name that the symlink has
resolvedItem.SetTo(item->EntryRef());
} else {
// shouldn't ever get here
TRESPASS();
entry_ref ref;
ref.set_name("");
resolvedItem.SetTo(&ref);
}
} else {
if (item->IsDirectory() && fSmallGroupIcon) {
SetDrawingMode(B_OP_OVER);
DrawBitmapAsync(fSmallGroupIcon, iconFrame);
} else
IconCache::sIconCache->Draw(item, this, iconFrame.LeftTop(),
kNormalIcon, B_MINI_ICON);
resolvedItem.SetTo(item->EntryRef());
}
// get the name of the resolved ref
// if this item points to /boot
// will retrieve the user name instead
const char *name = NULL;
if (resolvedItem.IsVolume()) {
BVolume volume(resolvedItem.NodeRef()->device);
char volumeName[B_FILE_NAME_LENGTH];
volume.GetName(volumeName);
name = volumeName;
} else if (item->IsSymLink()) {
// use the items symlink name
name = item->EntryRef()->name;
} else if (resolvedItem.Name() && resolvedItem.Name()[0] != '\0')
// use the items actual name
name = resolvedItem.Name();
else
name = kUntitledItemStr;
// truncate to fit appropriately
BString truncatedString(name);
fMenuFont->TruncateString(&truncatedString, B_TRUNCATE_END, textFrame.Width());
SetHighColor(kBlack);
SetFont(fMenuFont);
MovePenTo(textFrame.LeftBottom());
DrawString(truncatedString.String());
}
PopState();
}
void
TContentsMenu::InvalidateItem(int32 index)
{
BRect dummy, itemrect;
if (ItemFrame(index, &dummy, &dummy, &itemrect))
Invalidate(itemrect);
}
void
TContentsMenu::InvalidateAbsoluteItem(int32 index)
{
index -= fFirstItem;
if (index >= 0 && index < kMaxItemCount)
InvalidateItem(index);
}
void
TContentsMenu::KeyDown(const char *bytes, int32 numBytes)
{
if (IsEnabled() && IsFocus() && Window()->IsActive()) {
switch (bytes[0]) {
case B_DOWN_ARROW:
case B_LEFT_ARROW:
{
#ifdef ITEM_EDIT
StopItemEdit();
#endif
int32 selection = Value() + 1;
if (selection < fContentsList->CountItems()) {
SetValueNoUpdate(selection);
InvalidateAbsoluteItem(selection - 1);
InvalidateAbsoluteItem(selection);
Invoke();
}
//
// if the selection is the last item
// scroll the list down
// bottom item will be the selection
//
if (Value() - fFirstItem == kMaxItemCount)
Scroll(true);
}
break;
case B_UP_ARROW:
case B_RIGHT_ARROW:
{
#ifdef ITEM_EDIT
StopItemEdit();
#endif
int32 selection = Value()-1;
if (selection >= 0) {
SetValueNoUpdate(selection);
InvalidateAbsoluteItem(selection + 1);
InvalidateAbsoluteItem(selection);
Invoke();
}
if (Value() - fFirstItem < 0)
Scroll(false);
}
break;
case B_ENTER:
case B_SPACE:
OpenItem(Value());
break;
default:
BControl::KeyDown(bytes, numBytes);
break;
}
}
}
void
TContentsMenu::MessageReceived(BMessage *message)
{
if (message->WasDropped()) {
if (message->what == 'icon') {
BPoint where;
if (message->FindPoint("_drop_point_", &where) != B_OK) {
where.x = -1;
where.y = -1;
}
AddTempItem(where);
} else {
TFavoritesConfigWindow *window = dynamic_cast<TFavoritesConfigWindow*>(Window());
if (window)
window->AddRefs(message);
}
}
switch (message->what) {
// node monitor of be menu directory
case B_NODE_MONITOR:
{
// stash the entry_ref to the selected item
// use it after FillMenu to reselect the item
const entry_ref *oldref = ItemAt(Value())->EntryRef();
entry_ref ref;
if (!oldref)
ref.set_name("");
else
ref = *oldref;
EmptyMenu();
FillMenu(&fStartRef);
Select(&ref);
// checks for invalid ref
Invalidate();
// ask the window to update the buttons
Window()->PostMessage(kItemSelected);
}
break;
// down button
case kScrollUp:
#ifdef ITEM_EDIT
StopItemEdit();
#endif
Scroll(true);
break;
// up button
case kScrollDown:
#ifdef ITEM_EDIT
StopItemEdit();
#endif
Scroll(false);
break;
default:
BControl::MessageReceived(message);
break;
}
}
void
TContentsMenu::MouseDown(BPoint where)
{
#ifdef ITEM_EDIT
StopItemEdit();
#endif
bigtime_t clicktime;
get_click_speed(&clicktime);
bigtime_t diff = system_time() - fInitialClickTime;
if (diff < clicktime && fInitialClickRect.Contains(where)) {
OpenItem(Value());
return;
}
fInitialClickRect.Set(where.x, where.y, where.x, where.y);
fInitialClickRect.InsetBy(-2, -2);
fInitialClickTime = system_time();
StartTracking(where);
MakeFocus(true);
SetMouseEventMask(B_POINTER_EVENTS);
}
void
TContentsMenu::StartTracking(BPoint where)
{
SelectItemAt(where);
SetTracking(true);
}
void
TContentsMenu::StopTracking()
{
if (IsTracking())
SetTracking(false);
}
void
TContentsMenu::MouseUp(BPoint where)
{
StopTracking();
#ifdef ITEM_EDIT
BeginItemEdit(where);
#endif
BControl::MouseUp(where);
}
#ifdef ITEM_EDIT
void
TContentsMenu::BeginItemEdit(BPoint where)
{
bigtime_t clicktime;
get_click_speed(&clicktime);
bigtime_t diff = system_time() - fInitialClickTime;
if (where == fInitialClickLoc && (diff > clicktime)) {
BRect iconFrame, textFrame, itemFrame;
// !! get textFrame instead
// set viewcolor to menucolor
fItemIndex = ItemAt(where, &iconFrame, &textFrame, &itemFrame) + fFirstItem;
if (fItemIndex >= 0) {
// find the item
// add a textview
fEditingItem = true;
delete fEditingFld;
BRect trect(textFrame);
trect.OffsetTo(0, 0);
fEditingFld = new BTextView(textFrame, "edit", trect, B_FOLLOW_NONE);
AddChild(fEditingFld);
fEditingFld->SetViewColor(fHiliteColor);
fEditingFld->SetText(ItemAt(fItemIndex)->EntryRef()->name);
fEditingFld->SelectAll();
}
} else
fEditingItem = false;
}
#endif
#ifdef ITEM_EDIT
void
TContentsMenu::StopItemEdit()
{
if (fEditingItem && fEditingFld) {
if (fItemIndex > -1) {
const char *name = fEditingFld->Text();
const entry_ref *ref = ItemAt(fItemIndex)->EntryRef();
if (strcmp(ref->name, name) != 0) {
BEntry entry(ref);
if (entry.InitCheck() == B_OK && entry.Exists())
entry.Rename(name);
}
}
// dispose the textview
if (fEditingFld) {
fEditingFld->RemoveSelf();
delete fEditingFld;
fEditingFld = NULL;
fEditingItem = false;
fItemIndex = -1;
}
}
}
#endif
void
TContentsMenu::MouseMoved(BPoint where, uint32 code, const BMessage *message)
{
switch (code) {
case B_ENTERED_VIEW:
// if there is a message incoming
// then track with it for attempt at
// placement drop
// !! disabled for now
#if 0
if (message) {
SelectItemAt(where);
SetTracking(true);
}
#endif
break;
case B_INSIDE_VIEW:
if (IsTracking())
SelectItemAt(where);
break;
case B_EXITED_VIEW:
break;
}
BControl::MouseMoved(where, code, message);
}
void
TContentsMenu::GetPreferredSize(float *width, float *height)
{
// width should accomodate border, left gap, icon, separation, text, border
// height should acccomodate 14 itemheights + borders
*width = kLeftGutter + B_MINI_ICON + kItemGap
+ (fMenuFont->StringWidth("O") * 15)
+ (2 * kVerticalBorderSize);
*height = (2 * kScrollerHeight) // buttons
+ (kMaxItemCount * fItemHeight) // items
+ (2 * kHorizontalBorderSize) + 1; // border
}
void
TContentsMenu::ResizeToPreferred()
{
float width, height;
GetPreferredSize(&width, &height);
ResizeTo(width, height);
}
void
TContentsMenu::SetStartRef(const entry_ref *ref)
{
node_ref nref;
BEntry entry(&fStartRef);
if (entry.InitCheck() == B_OK) {
entry.GetNodeRef(&nref);
watch_node(&nref, B_STOP_WATCHING, this, Window());
}
fStartRef = *ref;
entry.SetTo(&fStartRef);
entry.GetNodeRef(&nref);
watch_node(&nref, B_WATCH_DIRECTORY, this, Window());
EmptyMenu();
FillMenu(&fStartRef);
Invalidate();
}
void
TContentsMenu::UpdateScrollers()
{
int32 count = fContentsList->CountItems();
if (count <= kMaxItemCount) {
fUpBtn->SetEnabled(false);
fDownBtn->SetEnabled(false);
} else {
fUpBtn->SetEnabled(fFirstItem != 0);
fDownBtn->SetEnabled(count-fFirstItem > kMaxItemCount);
}
}
void
TContentsMenu::Scroll(bool direction)
{
int32 count = fContentsList->CountItems();
if (count <= kMaxItemCount)
return;
bool needtoupdate = false;
if (direction && fDownBtn->IsEnabled()) { // down
if ((count - fFirstItem) > kMaxItemCount) {
fFirstItem++;
needtoupdate = true;
}
} else if (!direction && fUpBtn->IsEnabled()) { // up
if (fFirstItem >= 1) {
fFirstItem--;
needtoupdate = true;
}
}
if (needtoupdate) {
UpdateScrollers();
BRect dummy, highrect;
ItemFrame(0, &dummy, &dummy, &highrect);
highrect.bottom = highrect.bottom + fItemHeight * (kMaxItemCount - 2);
BRect lowrect = highrect.OffsetByCopy(0, fItemHeight);
if (direction) {
CopyBits(lowrect, highrect);
InvalidateItem(kMaxItemCount - 1);
} else {
CopyBits(highrect, lowrect);
InvalidateItem(0);
}
}
}
static int
CompareOne(const Model *model1, const Model *model2)
{
return strcasecmp(model1->Name(), model2->Name());
}
void
TContentsMenu::FillMenu(const entry_ref *ref)
{
if (!fContentsList)
fContentsList = new BObjectList<Model>(kMaxItemCount);
BEntry entry(ref);
if (entry.InitCheck() == B_OK && entry.Exists()) {
BDirectory dir(ref);
BEntry nextEntry;
while (dir.GetNextEntry(&nextEntry) == B_OK) {
// create a model for the actual item in the be folder
Model *model = new Model(&nextEntry);
fContentsList->AddItem(model);
}
fContentsList->SortItems(CompareOne);
}
int32 count = fContentsList->CountItems();
if (count <= kMaxItemCount)
fFirstItem = 0;
else if (count - fFirstItem < kMaxItemCount)
fFirstItem = count - kMaxItemCount;
UpdateScrollers();
}
void
TContentsMenu::EmptyMenu()
{
if (!fContentsList)
return;
int32 count = fContentsList->CountItems()-1;
for (int32 index = count ; index >= 0 ; index--) {
Model *item = fContentsList->ItemAt(index);
if (item) {
fContentsList->RemoveItem(item);
delete item;
}
}
}
/** returns frames for visible items */
bool
TContentsMenu::ItemFrame(int32 index, BRect *iconFrame, BRect *textFrame,
BRect *itemFrame) const
{
if (index >= kMaxItemCount || index < 0)
return false;
float halfheight = fItemHeight / 2;
float halficon = B_MINI_ICON / 2;
float halffont = fFontHeight / 2;
BPoint loc;
loc.x = kLeftGutter;
loc.y = fUpBtn->Frame().bottom + 1 + halfheight;
loc.y += index * fItemHeight;
iconFrame->Set(loc.x, loc.y - halficon, loc.x + B_MINI_ICON, loc.y + halficon);
textFrame->Set(loc.x + B_MINI_ICON + kItemGap, loc.y - halffont,
Bounds().right - 2, loc.y + halffont);
itemFrame->Set(2, loc.y - halfheight, Bounds().Width() - 2, loc.y + halfheight - 1);
return true;
}
/** returns index of visible item at location */
int32
TContentsMenu::ItemAt(BPoint where, BRect *iconFrame, BRect *textFrame,
BRect *itemFrame)
{
int32 count = fContentsList->CountItems();
count = (count < kMaxItemCount) ? count : kMaxItemCount;
for (int32 index = 0 ; index < count ; index++)
if (ItemFrame(index, iconFrame, textFrame, itemFrame)
&& itemFrame->Contains(where))
return index;
return -1;
}
/** returns entry_ref for item at absolute index */
const Model*
TContentsMenu::ItemAt(int32 index) const
{
return fContentsList->ItemAt(index);
}
void
TContentsMenu::SelectItemAt(BPoint where)
{
BRect iconFrame, textFrame, itemFrame;
int32 previousvalue = Value();
// get visible item
int32 index = ItemAt(where, &iconFrame, &textFrame, &itemFrame);
if (index <= -1) {
SetValueNoUpdate(-1);
Invoke();
} else {
// get offset to actual item selected
index += fFirstItem;
if (index != Value()) {
SetValueNoUpdate(index);
Invoke();
}
}
int32 newvalue = Value();
if (previousvalue != newvalue) {
InvalidateAbsoluteItem(previousvalue);
InvalidateAbsoluteItem(newvalue);
}
}
void
TContentsMenu::Select(const entry_ref *ref)
{
int32 select = -1;
if (ref && ref->name && ref->name[0] != '\0') {
int32 count = fContentsList->CountItems();
for (int32 index = 0 ; index < count ; index++) {
Model *item = fContentsList->ItemAt(index);
if (item) {
if (*ref == *(item->EntryRef())) {
select = index;
break;
}
}
}
}
SetValue(select);
Invoke();
}
void
TContentsMenu::OpenItem(int32 index)
{
if (Value() >= 0 && index >= 0 && index < fContentsList->CountItems()) {
Model *item = fContentsList->ItemAt(index);
// see if we have an item
// only actual directories can be traversed
// and only if their hierarchy lives in the Be folder
if (item && item->IsDirectory()) {
// if we have a double click message
// pass the new directory ref to the window
if (fDoubleClickMessage) {
if (fDoubleClickMessage->HasRef("current"))
fDoubleClickMessage->ReplaceRef("current", item->EntryRef());
else
fDoubleClickMessage->AddRef("current", item->EntryRef());
Invoke(fDoubleClickMessage);
}
}
}
}
int32
TContentsMenu::ItemCount() const
{
return fContentsList->CountItems();
}
static void
RemoveEntries(const entry_ref *ref)
{
// should this delete on DB created items?
BEntry entry(ref);
if (entry.InitCheck() == B_OK && entry.Exists()) {
// if its a directory
// check for contents and delete as necessary
if (entry.IsDirectory()) {
BDirectory dir(&entry);
BEntry nextEntry;
while (dir.GetNextEntry(&nextEntry) == B_OK) {
if (nextEntry.IsDirectory()) {
entry_ref nextref;
nextEntry.GetRef(&nextref);
RemoveEntries(&nextref);
} else
nextEntry.Remove();
}
}
entry.Remove();
}
}
void
TContentsMenu::RemoveItem(int32 index)
{
int32 count = ItemCount() - 1;
// index out of bounds
if (index < 0 || index > count)
return;
RemoveEntries(ItemAt(index)->EntryRef());
// index was last item
if (index == count) {
SetValue(index - 1);
Invoke();
}
}
void
TContentsMenu::AddTempItem(BPoint)
{
// !! need to make a fake item for editing
// defaults, now, to simply adding a folder
TFavoritesConfigWindow *window = dynamic_cast<TFavoritesConfigWindow*>(Window());
if (window)
window->AddNewGroup();
}
// ***************************************************************************************
// #pragma mark -
TScrollerButton::TScrollerButton(BRect frame, BMessage *message, bool direction)
: BControl(frame, "scroller", "scroller label", message,
B_FOLLOW_NONE, B_WILL_DRAW),
fDirection(direction),
fTicker(NULL)
{
}
void
TScrollerButton::AttachedToWindow()
{
BControl::AttachedToWindow();
menu_info minfo;
get_menu_info(&minfo);
fSelectedColor = tint_color(minfo.background_color, B_DARKEN_2_TINT);
SetViewColor(minfo.background_color);
fHiliteFrame = Bounds();
if (fDirection)
fHiliteFrame.bottom -= 6;
else
fHiliteFrame.top += 4;
}
void
TScrollerButton::DetachedFromWindow()
{
delete fTicker;
BControl::DetachedFromWindow();
}
void
TScrollerButton::Draw(BRect)
{
PushState();
if (Value() == 0 || !IsEnabled()) {
SetLowColor(ViewColor());
SetHighColor(ViewColor());
} else {
SetLowColor(fSelectedColor);
SetHighColor(fSelectedColor);
}
FillRect(fHiliteFrame);
// add the triangle
if (IsEnabled())
SetHighColor(kMediumGray);
else
SetHighColor(tint_color(kMediumGray, B_LIGHTEN_1_TINT));
float width = Bounds().Width();
int32 linecount = Bounds().IntegerHeight() - 9;
BPoint start(width / 2, fDirection ? 2 : Bounds().Height()-2);
BPoint finish(start);
for (int32 index = 0 ; index < linecount ; index++) {
StrokeLine(start, finish);
--start.x;
++finish.x;
if (fDirection) {
++start.y;
++finish.y;
} else {
--start.y;
--finish.y;
}
}
// add the top/bottom delimiter
SetHighColor(kLightGray);
float y = fDirection ? start.y + 2 : start.y - 3;
StrokeLine(BPoint(0, y), BPoint(width, y));
SetHighColor(kAlmostWhite);
y = fDirection ? start.y + 3 : start.y - 2;
StrokeLine(BPoint(0, y), BPoint(width, y));
PopState();
}
void
TScrollerButton::MouseDown(BPoint where)
{
if (IsEnabled() && fHiliteFrame.Contains(where)) {
SetValue(1);
Invoke();
SetTracking(true);
SetMouseEventMask(B_POINTER_EVENTS);
fTicker = new BMessageRunner(BMessenger(Target()), Message(), 120000);
}
}
void
TScrollerButton::MouseUp(BPoint)
{
SetValue(0);
delete fTicker;
fTicker = NULL;
if (IsTracking())
SetTracking(false);
}
void
TScrollerButton::MouseMoved(BPoint where, uint32 code, const BMessage *message)
{
switch (code) {
case B_EXITED_VIEW:
delete fTicker;
fTicker = NULL;
SetValue(0);
break;
case B_ENTERED_VIEW:
if (IsEnabled() && IsTracking()) {
SetValue(1);
Invoke();
fTicker = new BMessageRunner(BMessenger(Target()), Message(), 120000);
}
break;
default:
break;
}
BControl::MouseMoved(where, code, message);
}
// ***************************************************************************************
const char *kNewItemNameLabel = "New item name:";
const int32 kPanelWidth = 260;
const int32 kPanelHeight = 77;
NameItemPanel::NameItemPanel(BWindow *parent, const char *initialtext)
: BWindow(BRect(0, 0, kPanelWidth, kPanelHeight), "", B_MODAL_WINDOW,
B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_NOT_CLOSABLE),
fParent(parent)
{
MoveTo(-1024, -1024);
Show();
Lock();
AddParts(initialtext);
ResizeTo(Bounds().Width(), fCancelBtn->Frame().bottom + 10);
Unlock();
CenterWindowOnScreen(this);
}
NameItemPanel::~NameItemPanel()
{
}
void
NameItemPanel::MessageReceived(BMessage *message)
{
switch (message->what){
case 'done':
{
const char *text = fNameFld->Text();
if (!text || text[0] == '\0'){
BAlert *alert = new BAlert("",
"The new name is empty, please "
"enter a name", "Cancel", "OK", NULL,
B_WIDTH_AS_USUAL);
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
return;
}
BMessage nameChangeMessage(kNameChange);
nameChangeMessage.AddString("name", text);
fParent->PostMessage(&nameChangeMessage);
PostMessage(B_QUIT_REQUESTED);
}
break;
case 'canc':
fParent->PostMessage('canc');
PostMessage(B_QUIT_REQUESTED);
break;
default:
BWindow::MessageReceived(message);
break;
}
}
void
NameItemPanel::AddParts(const char *initialtext)
{
fBG = new BBox(Bounds(), "bg", B_FOLLOW_ALL, B_WILL_DRAW, B_NO_BORDER);
AddChild(fBG);
BRect rect(10, 10, Bounds().Width()-10, 11);
fNameFld = new BTextControl(rect, "", kNewItemNameLabel, "", NULL);
fBG->AddChild(fNameFld);
fNameFld->SetDivider(be_plain_font->StringWidth(kNewItemNameLabel) + 10);
fNameFld->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
if (initialtext && strlen(initialtext) > 0)
fNameFld->SetText(initialtext);
fNameFld->MakeFocus(true);
BTextView *textView = fNameFld->TextView();
if (textView)
textView->SetMaxBytes(32);
rect.right = Bounds().Width() - 10;
rect.left = rect.right - 75;
rect.top = fNameFld->Frame().bottom + 10;
rect.bottom = rect.top + 1;
fDoneBtn = new BButton(rect, "", "Change", new BMessage('done'),
B_FOLLOW_TOP | B_FOLLOW_RIGHT);
rect.right = rect.left - 10;
rect.left = rect.right - 75;
fCancelBtn = new BButton(rect, "", "Cancel", new BMessage('canc'),
B_FOLLOW_TOP | B_FOLLOW_RIGHT);
fBG->AddChild(fCancelBtn);
fBG->AddChild(fDoneBtn);
SetDefaultButton(fDoneBtn);
}