So here it goes..

I hope I have fixed all parts that don't follow our guidelines. (that python script was good start)
This are the app, I havn't change the file in clv those are from Santa gift bag

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33848 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Fredrik Modeen
2009-10-30 21:40:49 +00:00
parent be2b059224
commit 6c33367825
29 changed files with 5676 additions and 0 deletions
+1
View File
@@ -21,6 +21,7 @@ SubInclude HAIKU_TOP src preferences opengl ;
SubInclude HAIKU_TOP src preferences print ;
SubInclude HAIKU_TOP src preferences screen ;
SubInclude HAIKU_TOP src preferences screensaver ;
SubInclude HAIKU_TOP src preferences shortcuts ;
SubInclude HAIKU_TOP src preferences sounds ;
SubInclude HAIKU_TOP src preferences time ;
SubInclude HAIKU_TOP src preferences touchpad ;
+25
View File
@@ -0,0 +1,25 @@
SubDir HAIKU_TOP src preferences shortcuts ;
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src preferences shortcuts clv ] ;
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons input_server filters shortcut_catcher ] ;
Preference Shortcuts :
main.cpp
MetaKeyStateMap.cpp
ResizableButton.cpp
ShortcutsApp.cpp
ShortcutsSpec.cpp
ShortcutsWindow.cpp
# clv files
CLVColumn.cpp
CLVColumnLabelView.cpp
CLVListItem.cpp
ColumnListView.cpp
MouseWatcher.cpp
PrefilledBitmap.cpp
ScrollViewCorner.cpp
: be tracker libshortcuts_shared.a $(TARGET_LIBSTDC++)
: Shortcuts.rsrc
;
@@ -0,0 +1,91 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
*/
#include "MetaKeyStateMap.h"
#include <stdio.h>
#include <string.h>
#include "BitFieldTesters.h"
MetaKeyStateMap::MetaKeyStateMap()
:
fKeyName(NULL)
{
// User MUST call SetInfo() before using!
}
MetaKeyStateMap::MetaKeyStateMap(const char* name)
{
SetInfo(name);
}
void
MetaKeyStateMap::SetInfo(const char* keyName)
{
fKeyName = new char[strlen(keyName) + 1];
strcpy(fKeyName, keyName);
}
MetaKeyStateMap::~MetaKeyStateMap()
{
delete [] fKeyName;
int nr = fStateDescs.CountItems();
for (int i = 0; i < nr; i++)
delete [] ((const char*) fStateDescs.ItemAt(i));
nr = fStateTesters.CountItems();
for (int j = 0; j < nr; j++)
delete ((BitFieldTester*) fStateTesters.ItemAt(j));
// _stateBits are stored in-line, no need to delete them
}
void
MetaKeyStateMap::AddState(const char* d, const BitFieldTester* t)
{
char* copy = new char[strlen(d) + 1];
strcpy(copy, d);
fStateDescs.AddItem(copy);
fStateTesters.AddItem((void *)t);
}
int
MetaKeyStateMap::GetNumStates() const
{
return fStateTesters.CountItems();
}
const BitFieldTester*
MetaKeyStateMap::GetNthStateTester(int stateNum) const
{
return ((const BitFieldTester*) fStateTesters.ItemAt(stateNum));
}
const char*
MetaKeyStateMap::GetNthStateDesc(int stateNum) const
{
return ((const char*) fStateDescs.ItemAt(stateNum));
}
const char*
MetaKeyStateMap::GetName() const
{
return fKeyName;
}
@@ -0,0 +1,66 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
*/
#ifndef MetaKeyStateMap_h
#define MetaKeyStateMap_h
#include <SupportDefs.h>
#include <List.h>
class BitFieldTester;
// This class defines a set of possible chording states (e.g. "Left only",
// "Right only", "Both", "Either") for a meta-key (e.g. Shift), and the
// description strings and qualifier bit-chords that go with them.
class MetaKeyStateMap {
public:
// Note: You MUST call SetInfo() directly after using this ctor!
MetaKeyStateMap();
// Creates a MetaKeyStateMap with the give name
// (e.g. "Shift" or "Ctrl")
MetaKeyStateMap(const char* keyName);
~MetaKeyStateMap();
// For when you have to use the default ctor
void SetInfo(const char* keyName);
// (tester) becomes property of this map!
void AddState(const char* desc, const BitFieldTester* tester);
// Returns the name of the meta-key (e.g. "Ctrl")
const char* GetName() const;
// Returns the number of possible states contained in this
// MetaKeyStateMap.
int GetNumStates() const;
// Returns a BitFieldTester that tests for the nth state's
// presence.
const BitFieldTester* GetNthStateTester(int stateNum) const;
// Returns a textual description of the nth state (e.g. "Left")
const char* GetNthStateDesc(int stateNum) const;
private:
// e.g. "Alt" or "Ctrl"
char* fKeyName;
// list of strings e.g. "Left" or "Both"
BList fStateDescs;
// list of BitFieldTesters for testing bits of modifiers
// in state
BList fStateTesters;
};
#endif
@@ -0,0 +1,35 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
*/
#include "ResizableButton.h"
ResizableButton::ResizableButton(BRect parentFrame, BRect frame,
const char* name, const char* label, BMessage* msg)
:
BButton(frame, name, label, msg, B_FOLLOW_BOTTOM)
{
float width = parentFrame.right - parentFrame.left;
float height = parentFrame.bottom - parentFrame.top;
fPercentages.left = frame.left / width;
fPercentages.top = frame.top / height;
fPercentages.right = frame.right / width;
fPercentages.bottom = frame.bottom / height;
}
void
ResizableButton::ChangeToNewSize(float newWidth, float newHeight)
{
float newX = fPercentages.left* newWidth;
float newW = (fPercentages.right* newWidth) - newX;
BRect b = Frame();
MoveBy(newX - b.left, 0);
ResizeTo(newW, b.bottom - b.top);
Invalidate();
}
@@ -0,0 +1,30 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
*/
#ifndef ResizableButton_h
#define ResizableButton_h
#include <Message.h>
#include <Rect.h>
#include <View.h>
#include <Button.h>
//Just like a regular BButton, but with a handy resize method added
class ResizableButton : public BButton {
public:
ResizableButton(BRect parentFrame, BRect frame,
const char* name, const char* label,
BMessage* msg);
virtual void ChangeToNewSize(float newWidth, float newHeight);
private:
BRect fPercentages;
};
#endif
Binary file not shown.
@@ -0,0 +1,48 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
* Fredrik Modéen
*/
#include "ShortcutsApp.h"
#include "Alert.h"
#include "ShortcutsWindow.h"
#define APPLICATION_SIGNATURE "application/x-vnd.ShortcutsKeys"
ShortcutsApp::ShortcutsApp()
:
BApplication(APPLICATION_SIGNATURE)
{
}
void
ShortcutsApp::ReadyToRun()
{
ShortcutsWindow* window = new ShortcutsWindow();
window->Show();
}
ShortcutsApp::~ShortcutsApp()
{
}
void
ShortcutsApp::AboutRequested()
{
BAlert* alert = new BAlert("About Shortcuts",
"Shortcuts v1.28(SpicyKeys v1.28)\nby Jeremy Friesner"
, "Ok");
alert->Go();
}
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
* Fredrik Modéen
*/
#ifndef ShortcutsApp_h
#define ShortcutsApp_h
#include <Application.h>
class ShortcutsApp : public BApplication {
public:
ShortcutsApp();
~ShortcutsApp();
virtual void ReadyToRun();
virtual void AboutRequested();
};
#endif
+838
View File
@@ -0,0 +1,838 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
*/
#include "ShortcutsSpec.h"
#include <ctype.h>
#include <stdio.h>
#include <Region.h>
#include <Window.h>
#include <Directory.h>
#include <Path.h>
#include <NodeInfo.h>
#include <Beep.h>
#include "ColumnListView.h"
#include "Colors.h"
#include "MetaKeyStateMap.h"
#include "BitFieldTesters.h"
#include "CommandActuators.h"
#include "ParseCommandLine.h"
#define CLASS "ShortcutsSpec : "
const float _height = 20.0f;
static MetaKeyStateMap _metaMaps[ShortcutsSpec::NUM_META_COLUMNS];
static bool _metaMapsInitialized = false;
static bool _fontCached = false;
static BFont _viewFont;
static float _fontHeight;
static BBitmap * _actuatorBitmaps[2];
// These meta-keys are pretty standard
#define SHIFT_NAME "Shift"
#define CONTROL_NAME "Control"
// These meta-keys have different names on an Intel keyboard
#if __INTEL__
#define OPTION_NAME "Window"
#define COMMAND_NAME "Alt"
#else
#define OPTION_NAME "Option"
#define COMMAND_NAME "Command"
#endif
#define ICON_BITMAP_RECT BRect(0.0f, 0.0f, 15.0f, 15.0f)
#define ICON_BITMAP_SPACE B_COLOR_8_BIT
// Returns the (pos)'th char in the string, or '\0' if (pos) if off the end of
// the string
static char GetLetterAt(const char* str, int pos);
static char
GetLetterAt(const char* str, int pos)
{
for (int i = 0; i < pos; i++)
if (str[i] == '\0')
return '\0';
return str[pos];
}
// Setup the states in a standard manner for a pair of meta-keys.
static void
SetupStandardMap(MetaKeyStateMap& map, const char* name, uint32 both,
uint32 left, uint32 right)
{
map.SetInfo(name);
// In this state, neither key may be pressed.
map.AddState("(None)", new HasBitsFieldTester(0, both));
// Here, either may be pressed. (Remember both is NOT a 2-bit chord, it's
// another bit entirely)
map.AddState("Either", new HasBitsFieldTester(both));
// Here, only the left may be pressed
map.AddState("Left", new HasBitsFieldTester(left, right));
// Here, only the right may be pressed
map.AddState("Right", new HasBitsFieldTester(right, left));
// Here, both must be pressed.
map.AddState("Both", new HasBitsFieldTester(left | right));
}
MetaKeyStateMap & GetNthKeyMap(int which)
{
return _metaMaps[which];
}
static BBitmap* MakeActuatorBitmap(bool lit);
static BBitmap*
MakeActuatorBitmap(bool lit)
{
BBitmap* map = new BBitmap(ICON_BITMAP_RECT, ICON_BITMAP_SPACE, true);
const rgb_color yellow = {255, 255, 0};
const rgb_color red = {200, 200, 200};
const rgb_color black = {0, 0, 0};
const BPoint points[10] = {
BPoint(8, 0), BPoint(9.8, 5.8), BPoint(16, 5.8),
BPoint(11, 9.0), BPoint(13, 16), BPoint(8, 11),
BPoint(3, 16), BPoint(5, 9.0), BPoint(0, 5.8),
BPoint(6.2, 5.8) };
BView* view = new BView(BRect(0, 0, 16, 16), NULL, B_FOLLOW_ALL_SIDES, 0L);
map->AddChild(view);
map->Lock();
view->SetHighColor(B_TRANSPARENT_32_BIT);
view->FillRect(ICON_BITMAP_RECT);
view->SetHighColor(lit ? yellow : red);
view->FillPolygon(points, 10);
view->SetHighColor(black);
view->StrokePolygon(points, 10);
map->Unlock();
map->RemoveChild(view);
delete view;
return map;
}
void InitializeMetaMaps()
{
_metaMapsInitialized = true;
SetupStandardMap(_metaMaps[ShortcutsSpec::SHIFT_COLUMN_INDEX], SHIFT_NAME,
B_SHIFT_KEY, B_LEFT_SHIFT_KEY, B_RIGHT_SHIFT_KEY);
SetupStandardMap(_metaMaps[ShortcutsSpec::CONTROL_COLUMN_INDEX],
CONTROL_NAME, B_CONTROL_KEY, B_LEFT_CONTROL_KEY, B_RIGHT_CONTROL_KEY);
SetupStandardMap(_metaMaps[ShortcutsSpec::COMMAND_COLUMN_INDEX],
COMMAND_NAME, B_COMMAND_KEY, B_LEFT_COMMAND_KEY, B_RIGHT_COMMAND_KEY);
SetupStandardMap(_metaMaps[ShortcutsSpec::OPTION_COLUMN_INDEX], OPTION_NAME
, B_OPTION_KEY, B_LEFT_OPTION_KEY, B_RIGHT_OPTION_KEY);
_actuatorBitmaps[0] = MakeActuatorBitmap(false);
_actuatorBitmaps[1] = MakeActuatorBitmap(true);
}
ShortcutsSpec::ShortcutsSpec(const char* cmd)
:
CLVListItem(0, false, false, _height),
fCommand(NULL),
fTextOffset(0),
fBitmap(ICON_BITMAP_RECT, ICON_BITMAP_SPACE),
fLastBitmapName(NULL),
fBitmapValid(false),
fKey(0),
fCursorPtsValid(false)
{
for (int i = 0; i < NUM_META_COLUMNS; i++)
fMetaCellStateIndex[i] = 0;
SetCommand(cmd);
}
ShortcutsSpec::ShortcutsSpec(const ShortcutsSpec& from)
:
CLVListItem(0, false, false, _height),
fCommand(NULL),
fTextOffset(from.fTextOffset),
fBitmap(ICON_BITMAP_RECT, ICON_BITMAP_SPACE),
fLastBitmapName(NULL),
fBitmapValid(false),
fKey(from.fKey),
fCursorPtsValid(false)
{
for (int i = 0; i < NUM_META_COLUMNS; i++)
fMetaCellStateIndex[i] = from.fMetaCellStateIndex[i];
SetCommand(from.fCommand);
SetSelectedColumn(from.GetSelectedColumn());
}
ShortcutsSpec::ShortcutsSpec(BMessage* from)
:
CLVListItem(0, false, false, _height),
fCommand(NULL),
fTextOffset(0),
fBitmap(ICON_BITMAP_RECT, ICON_BITMAP_SPACE),
fLastBitmapName(NULL),
fBitmapValid(false),
fCursorPtsValid(false)
{
const char* temp;
if (from->FindString("command", &temp) != B_NO_ERROR) {
printf(CLASS " Error, no command string in archive BMessage!\n");
temp = "";
}
SetCommand(temp);
if (from->FindInt32("key", (int32*) &fKey) != B_NO_ERROR)
printf(CLASS " Error, no key int32 in archive BMessage!\n");
for (int i = 0; i < NUM_META_COLUMNS; i++)
if (from->FindInt32("mcidx", i, (int32*)&fMetaCellStateIndex[i])
!= B_NO_ERROR)
printf(CLASS " Error, no modifiers int32 in archive BMessage!\n");
}
void
ShortcutsSpec::SetCommand(const char* command)
{
delete[] fCommand; // out with the old (if any)...
fCommandLen = strlen(command) + 1;
fCommandNul = fCommandLen - 1;
fCommand = new char[fCommandLen];
strcpy(fCommand, command);
_UpdateIconBitmap();
}
const char*
ShortcutsSpec::GetColumnName(int i)
{
return _metaMaps[i].GetName();
}
status_t
ShortcutsSpec::Archive(BMessage* into, bool deep) const
{
status_t ret = BArchivable::Archive(into, deep);
if (ret != B_NO_ERROR)
return ret;
into->AddString("class", "ShortcutsSpec");
// These fields are for our prefs panel's benefit only
into->AddString("command", fCommand);
into->AddInt32("key", fKey);
// Assemble a BitFieldTester for the input_server add-on to use...
MinMatchFieldTester test(NUM_META_COLUMNS, false);
for (int i = 0; i < NUM_META_COLUMNS; i++) {
// for easy parsing by prefs applet on load-in
into->AddInt32("mcidx", fMetaCellStateIndex[i]);
test.AddSlave(_metaMaps[i].GetNthStateTester(fMetaCellStateIndex[i]));
}
BMessage testerMsg;
ret = test.Archive(&testerMsg);
if (ret != B_NO_ERROR)
return ret;
into->AddMessage("modtester", &testerMsg);
// And also create a CommandActuator for the input_server add-on to execute
CommandActuator* act = CreateCommandActuator(fCommand);
BMessage actMsg;
ret = act->Archive(&actMsg);
if (ret != B_NO_ERROR)
return ret;
delete act;
into->AddMessage("act", &actMsg);
return ret;
}
static bool IsValidActuatorName(const char* c);
static bool
IsValidActuatorName(const char* c)
{
return ((strcmp(c, "InsertString") == 0) ||
(strcmp(c, "MoveMouse") == 0) ||
(strcmp(c, "MoveMouseTo") == 0) ||
(strcmp(c, "MouseButton") == 0) ||
(strcmp(c, "LaunchHandler") == 0) || // new for v1.21 --jaf
(strcmp(c, "Multi") == 0) || // new for v1.24 --jaf
(strcmp(c, "MouseDown") == 0) || // new for v1.24 --jaf
(strcmp(c, "MouseUp") == 0) || // new for v1.24 --jaf
(strcmp(c, "SendMessage") == 0) || // new for v1.25 --jaf
(strcmp(c, "Beep") == 0));
}
BArchivable*
ShortcutsSpec::Instantiate(BMessage* from)
{
bool validateOK = false;
if (validate_instantiation(from, "ShortcutsSpec"))
validateOK = true;
else //test the old one.
if (validate_instantiation(from, "SpicyKeysSpec"))
validateOK = true;
if (!validateOK)
return NULL;
return new ShortcutsSpec(from);
}
ShortcutsSpec::~ShortcutsSpec()
{
delete[] fCommand;
delete[] fLastBitmapName;
}
void
ShortcutsSpec::_CacheViewFont(BView* owner)
{
if (_fontCached == false) {
_fontCached = true;
owner->GetFont(&_viewFont);
font_height fh;
_viewFont.GetHeight(&fh);
_fontHeight = fh.ascent - fh.descent;
}
}
void
ShortcutsSpec::DrawItemColumn(BView* owner, BRect item_column_rect,
int32 column_index, bool columnSelected, bool complete)
{
const float STRING_COLUMN_LEFT_MARGIN = 25.0f; // 16 for the icon,+9 empty
rgb_color color;
bool selected = IsSelected();
if (selected)
color = columnSelected ? BeBackgroundGrey : BeListSelectGrey;
else
color = BeInactiveControlGrey;
owner->SetLowColor(color);
owner->SetDrawingMode(B_OP_COPY);
owner->SetHighColor(color);
owner->FillRect(item_column_rect);
const char* text = GetCellText(column_index);
if (text == NULL)
return;
float textWidth = _viewFont.StringWidth(text);
BPoint point;
rgb_color lowColor = color;
if (column_index == STRING_COLUMN_INDEX) {
// left justified
point.Set(item_column_rect.left + STRING_COLUMN_LEFT_MARGIN,
item_column_rect.top + fTextOffset);
item_column_rect.left = point.x;//keep text from drawing into icon area
// scroll if too wide
float rectWidth = item_column_rect.Width() - STRING_COLUMN_LEFT_MARGIN;
float extra = textWidth - rectWidth;
if (extra > 0.0f)
point.x -= extra;
} else {
if ((column_index < NUM_META_COLUMNS) && (text[0] == '('))
return; // don't draw for this ...
if ((column_index <= NUM_META_COLUMNS) && (text[0] == '\0'))
return; // don't draw for this ...
// centered
point.Set((item_column_rect.left + item_column_rect.right) / 2.0,
item_column_rect.top + fTextOffset);
_CacheViewFont(owner);
point.x -= textWidth / 2.0f;
}
BRegion Region;
Region.Include(item_column_rect);
owner->ConstrainClippingRegion(&Region);
if (column_index != STRING_COLUMN_INDEX) {
const float KEY_MARGIN = 3.0f;
const float CORNER_RADIUS = 3.0f;
_CacheViewFont(owner);
// How about I draw a nice "key" background for this one?
BRect textRect(point.x - KEY_MARGIN, (point.y-_fontHeight) - KEY_MARGIN
, point.x + textWidth + KEY_MARGIN - 2.0f, point.y + KEY_MARGIN);
if (column_index == KEY_COLUMN_INDEX)
lowColor = ReallyLightPurple;
else
lowColor = LightYellow;
owner->SetHighColor(lowColor);
owner->FillRoundRect(textRect, CORNER_RADIUS, CORNER_RADIUS);
owner->SetHighColor(Black);
owner->StrokeRoundRect(textRect, CORNER_RADIUS, CORNER_RADIUS);
}
owner->SetHighColor(Black);
owner->SetLowColor(lowColor);
owner->DrawString(text, point);
// with a cursor at the end if highlighted
if (column_index == STRING_COLUMN_INDEX) {
// Draw cursor
if ((columnSelected) && (selected)) {
point.x += textWidth;
point.y += (fTextOffset / 4.0f);
BPoint pt2 = point;
pt2.y -= fTextOffset;
owner->StrokeLine(point, pt2);
fCursorPt1 = point;
fCursorPt2 = pt2;
fCursorPtsValid = true;
}
BRegion bitmapRegion;
item_column_rect.left -= (STRING_COLUMN_LEFT_MARGIN - 4.0f);
item_column_rect.right = item_column_rect.left + 16.0f;
item_column_rect.top += 3.0f;
item_column_rect.bottom = item_column_rect.top + 16.0f;
bitmapRegion.Include(item_column_rect);
owner->ConstrainClippingRegion(&bitmapRegion);
owner->SetDrawingMode(B_OP_OVER);
if ((fCommand != NULL) && (fCommand[0] == '*'))
owner->DrawBitmap(_actuatorBitmaps[fBitmapValid ? 1 : 0],
ICON_BITMAP_RECT, item_column_rect);
else
// Draw icon, if any
if (fBitmapValid)
owner->DrawBitmap(&fBitmap, ICON_BITMAP_RECT,
item_column_rect);
}
owner->SetDrawingMode(B_OP_COPY);
owner->ConstrainClippingRegion(NULL);
}
void
ShortcutsSpec::Update(BView* owner, const BFont* font)
{
CLVListItem::Update(owner, font);
font_height FontAttributes;
be_plain_font->GetHeight(&FontAttributes);
float fontHeight = ceil(FontAttributes.ascent) +
ceil(FontAttributes.descent);
fTextOffset = ceil(FontAttributes.ascent) + (Height() - fontHeight) / 2.0;
}
const char*
ShortcutsSpec::GetCellText(int whichColumn) const
{
const char* temp = ""; // default
switch(whichColumn)
{
case KEY_COLUMN_INDEX:
{
if ((fKey > 0) && (fKey <= 0xFF)) {
temp = GetKeyName(fKey);
if (temp == NULL)
temp = "";
} else if (fKey > 0xFF) {
sprintf(fScratch, "#%x", fKey);
return fScratch;
}
}
break;
case STRING_COLUMN_INDEX:
temp = fCommand;
break;
default:
if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS))
temp = _metaMaps[whichColumn].GetNthStateDesc(
fMetaCellStateIndex[whichColumn]);
break;
}
return temp;
}
bool
ShortcutsSpec::ProcessColumnMouseClick(int whichColumn)
{
if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS)) {
// same as hitting space for these columns: cycle entry
const char temp = B_SPACE;
// 3rd arg isn't correct but it isn't read for this case anyway
return ProcessColumnKeyStroke(whichColumn, &temp, 0);
}
return false;
}
bool
ShortcutsSpec::ProcessColumnTextString(int whichColumn, const char* string)
{
switch(whichColumn) {
case STRING_COLUMN_INDEX:
SetCommand(string);
return true;
break;
case KEY_COLUMN_INDEX:
{
fKey = FindKeyCode(string);
return true;
break;
}
default:
return ProcessColumnKeyStroke(whichColumn, string, 0);
}
}
bool
ShortcutsSpec::_AttemptTabCompletion()
{
bool ret = false;
int32 argc;
char** argv = ParseArgvFromString(fCommand, argc);
if (argc > 0) {
// Try to complete the path partially expressed in the last argument!
char* arg = argv[argc - 1];
char* fileFragment = strrchr(arg, '/');
if (fileFragment) {
const char* directoryName = (fileFragment == arg) ? "/" : arg;
*fileFragment = '\0';
fileFragment++;
int fragLen = strlen(fileFragment);
BDirectory dir(directoryName);
if (dir.InitCheck() == B_NO_ERROR) {
BEntry nextEnt;
BPath nextPath;
BList matchList;
int maxEntryLen = 0;
// Read in all the files in the directory whose names start
// with our fragment.
while (dir.GetNextEntry(&nextEnt) == B_NO_ERROR) {
if (nextEnt.GetPath(&nextPath) == B_NO_ERROR) {
char* filePath = strrchr(nextPath.Path(), '/') + 1;
if (strncmp(filePath, fileFragment, fragLen) == 0) {
int len = strlen(filePath);
if (len > maxEntryLen)
maxEntryLen = len;
char* newStr = new char[len + 1];
strcpy(newStr, filePath);
matchList.AddItem(newStr);
}
}
}
// Now slowly extend our keyword to its full length, counting
// numbers of matches at each step. If the match list length
// is 1, we can use that whole entry. If it's greater than one
// , we can use just the match length.
int matchLen = matchList.CountItems();
if (matchLen > 0) {
int i;
BString result(fileFragment);
for (i = fragLen; i < maxEntryLen; i++) {
// See if all the matching entries have the same letter
// in the next position... if so, we can go farther.
char commonLetter = '\0';
for (int j = 0; j < matchLen; j++) {
char nextLetter = GetLetterAt(
(char*)matchList.ItemAt(j), i);
if (commonLetter == '\0')
commonLetter = nextLetter;
if ((commonLetter != '\0')
&& (commonLetter != nextLetter)) {
commonLetter = '\0';// failed;
beep();
break;
}
}
if (commonLetter == '\0')
break;
else
result.Append(commonLetter, 1);
}
// Free all the strings we allocated
for (int k = 0; k < matchLen; k++)
delete [] ((char*)matchList.ItemAt(k));
DoStandardEscapes(result);
BString wholeLine;
for (int l = 0; l < argc - 1; l++) {
wholeLine += argv[l];
wholeLine += " ";
}
BString file(directoryName);
DoStandardEscapes(file);
if (directoryName[strlen(directoryName) - 1] != '/')
file += "/";
file += result;
// Remove any trailing slash...
const char* fileStr = file.String();
if (fileStr[strlen(fileStr)-1] == '/')
file.RemoveLast("/");
// And re-append it iff the file is a dir.
BDirectory testFileAsDir(file.String());
if ((strcmp(file.String(), "/") != 0)
&& (testFileAsDir.InitCheck() == B_NO_ERROR))
file.Append("/");
wholeLine += file;
SetCommand(wholeLine.String());
ret = true;
}
}
*(fileFragment - 1) = '/';
}
}
FreeArgv(argv);
return ret;
}
bool
ShortcutsSpec::ProcessColumnKeyStroke(int whichColumn, const char* bytes,
int32 key)
{
bool ret = false;
switch(whichColumn) {
case KEY_COLUMN_INDEX:
if (key != -1) {
if (fKey != key) {
fKey = key;
ret = true;
}
}
break;
case STRING_COLUMN_INDEX:
{
switch(bytes[0]) {
case B_BACKSPACE:
case B_DELETE:
if (fCommandNul > 0) {
//trim a char off the string
fCommand[fCommandNul - 1] = '\0';
fCommandNul--; // note new nul position
ret = true;
_UpdateIconBitmap();
}
break;
case B_TAB:
if (_AttemptTabCompletion()) {
_UpdateIconBitmap();
ret = true;
} else
beep();
break;
default:
{
int newCharLen = strlen(bytes);
if ((newCharLen > 0) && (bytes[0] >= ' ')) {
bool reAllocString = false;
// Make sure we have enough room in our command string
// to add these chars...
while (fCommandLen - fCommandNul <= newCharLen) {
reAllocString = true;
// enough for a while...
fCommandLen = (fCommandLen + 10) * 2;
}
if (reAllocString) {
char* temp = new char[fCommandLen];
strcpy(temp, fCommand);
delete [] fCommand;
fCommand = temp;
// fCommandNul is still valid since it's an offset
// and the string length is the same for now
}
// Here we should be guaranteed enough room.
strncat(fCommand, bytes, fCommandLen);
fCommandNul += newCharLen;
ret = true;
_UpdateIconBitmap();
}
}
}
}
break;
default:
if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS)) {
MetaKeyStateMap * map = &_metaMaps[whichColumn];
int curState = fMetaCellStateIndex[whichColumn];
int origState = curState;
int numStates = map->GetNumStates();
switch(bytes[0])
{
case B_RETURN:
// cycle to the previous state
curState = (curState + numStates - 1) % numStates;
break;
case B_SPACE:
// cycle to the next state
curState = (curState + 1) % numStates;
break;
default:
{
// Go to the state starting with the given letter, if
// any
char letter = bytes[0];
if (islower(letter))
letter = toupper(letter); // convert to upper case
if ((letter == B_BACKSPACE) || (letter == B_DELETE))
letter = '(';//so space bar will blank out an entry
for (int i = 0; i < numStates; i++) {
const char* desc = map->GetNthStateDesc(i);
if (desc) {
if (desc[0] == letter) {
curState = i;
break;
}
} else
printf("Error, NULL state description?\n");
}
}
break;
}
fMetaCellStateIndex[whichColumn] = curState;
if (curState != origState)
ret = true;
}
break;
}
return ret;
}
int
ShortcutsSpec::MyCompare(const CLVListItem* a_Item1, const CLVListItem* a_Item2
, int32 KeyColumn)
{
ShortcutsSpec* left = (ShortcutsSpec*) a_Item1;
ShortcutsSpec* right = (ShortcutsSpec*) a_Item2;
int ret = strcmp(left->GetCellText(KeyColumn),
right->GetCellText(KeyColumn));
return (ret > 0) ? 1 : ((ret == 0) ? 0 : -1);
}
void
ShortcutsSpec::Pulse(BView* owner)
{
if ((fCursorPtsValid)&&(owner->Window()->IsActive())) {
rgb_color prevColor = owner->HighColor();
rgb_color backgroundColor = (GetSelectedColumn() ==
STRING_COLUMN_INDEX) ? BeBackgroundGrey : BeListSelectGrey;
rgb_color barColor = ((GetSelectedColumn() == STRING_COLUMN_INDEX)
&& ((system_time() % 1000000) > 500000)) ? Black : backgroundColor;
owner->SetHighColor(barColor);
owner->StrokeLine(fCursorPt1, fCursorPt2);
owner->SetHighColor(prevColor);
}
}
void
ShortcutsSpec::_UpdateIconBitmap()
{
BString firstWord = ParseArgvZeroFromString(fCommand);
// Only need to change if the first word has changed...
if ((fLastBitmapName == NULL) || (firstWord.Length() == 0) ||
(firstWord.Compare(fLastBitmapName))) {
if (firstWord.ByteAt(0) == '*')
fBitmapValid = IsValidActuatorName(&firstWord.String()[1]);
else {
fBitmapValid = false; // default till we prove otherwise!
if (firstWord.Length() > 0) {
delete [] fLastBitmapName;
fLastBitmapName = new char[firstWord.Length() + 1];
strcpy(fLastBitmapName, firstWord.String());
BEntry progEntry(fLastBitmapName, true);
if ((progEntry.InitCheck() == B_NO_ERROR)
&& (progEntry.Exists())) {
BNode progNode(&progEntry);
if (progNode.InitCheck() == B_NO_ERROR) {
BNodeInfo progNodeInfo(&progNode);
if ((progNodeInfo.InitCheck() == B_NO_ERROR)
&& (progNodeInfo.GetTrackerIcon(&fBitmap, B_MINI_ICON)
== B_NO_ERROR))
fBitmapValid = fBitmap.IsValid();
}
}
}
}
}
}
+104
View File
@@ -0,0 +1,104 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
*/
#ifndef ShortcutsSpec_h
#define ShortcutsSpec_h
#include <Bitmap.h>
#include "CLVListItem.h"
#include "KeyInfos.h"
class CommandActuator;
class MetaKeyStateMap;
MetaKeyStateMap & GetNthKeyMap(int which);
void InitializeMetaMaps();
/* Objects of this class represent one hotkey "entry" in the preferences
* ListView. Each ShortcutsSpec contains the info necessary to generate both
* the proper GUI display, and the proper BitFieldTester and CommandActuator
* object for the ShortcutsCatcher add-on to use.
*/
class ShortcutsSpec : public CLVListItem {
public:
ShortcutsSpec(const char* command);
ShortcutsSpec(const ShortcutsSpec& copyMe);
ShortcutsSpec(BMessage* from);
~ShortcutsSpec();
virtual status_t Archive(BMessage* into, bool deep = true) const;
virtual void Pulse(BView* owner);
static BArchivable* Instantiate(BMessage* from);
void Update(BView* owner, const BFont* font);
const char* GetCellText(int whichColumn) const;
void SetCommand(const char* commandStr);
virtual void DrawItemColumn(BView* owner, BRect item_column_rect, int32 column_index, bool columnSelected, bool complete);
static int MyCompare(const CLVListItem* a_Item1,
const CLVListItem* a_Item2, int32 KeyColumn);
// Returns the name of the Nth Column.
static const char* GetColumnName(int index);
// Update this spec's state in response to a keystroke to the given
// column. Returns true iff a change occurred.
bool ProcessColumnKeyStroke(int whichColumn,
const char* bytes, int32 key);
// Same as ProcessColumnKeyStroke, but for a mouse click instead.
bool ProcessColumnMouseClick(int whichColumn);
// Same as ProcessColumnKeyStroke, but for a text string instead.
bool ProcessColumnTextString(int whichColumn,
const char* string);
int32 GetSelectedColumn() const {return fSelectedColumn;}
void SetSelectedColumn(int32 i) {fSelectedColumn = i;}
// default layout of columns is set in here.
enum {
SHIFT_COLUMN_INDEX = 0,
CONTROL_COLUMN_INDEX = 1,
COMMAND_COLUMN_INDEX = 2,
OPTION_COLUMN_INDEX = 3,
NUM_META_COLUMNS = 4, // shift, control, command, option, for now
KEY_COLUMN_INDEX = NUM_META_COLUMNS,
STRING_COLUMN_INDEX = 5
};
private:
void _CacheViewFont(BView* owner);
bool _AttemptTabCompletion();
// call this to ensure the icon is up-to-date
void _UpdateIconBitmap();
char* fCommand;
uint32 fCommandLen; // number of bytes in fCommand buffer
uint32 fCommandNul; // index of the NUL byte in fCommand
float fTextOffset;
// icon for associated program. Invalid if none available.
BBitmap fBitmap;
char* fLastBitmapName;
bool fBitmapValid;
uint32 fKey;
int32 fMetaCellStateIndex[NUM_META_COLUMNS];
BPoint fCursorPt1;
BPoint fCursorPt2;
bool fCursorPtsValid;
mutable char fScratch[50];
int32 fSelectedColumn;
};
#endif
@@ -0,0 +1,722 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
* Fredrik Modéen
*/
#include "ShortcutsWindow.h"
#include <math.h>
#include <stdio.h>
#include <Alert.h>
#include <Application.h>
#include <Clipboard.h>
#include <MessageFilter.h>
#include <Menu.h>
#include <MenuItem.h>
#include <MenuBar.h>
#include <ScrollBar.h>
#include <ScrollView.h>
#include <String.h>
#include <Input.h>
#include <PopUpMenu.h>
#include <File.h>
#include <Path.h>
#include <FindDirectory.h>
#include "ColumnListView.h"
#include "KeyInfos.h"
#include "ShortcutsSpec.h"
#include "ParseCommandLine.h"
#include "MetaKeyStateMap.h"
#include "ShortcutsFilterConstants.h"
// Window sizing constraints
#define MIN_WIDTH 600
#define MIN_HEIGHT 130
#define MAX_WIDTH 65535
#define MAX_HEIGHT 65535
// Default window position
#define WINDOW_START_X 30
#define WINDOW_START_Y 100
#define ERROR "Shortcuts Error"
#define WARNING "Shortcuts warning"
// Global constants for Shortcuts
#define V_SPACING 5 // vertical spacing between GUI components
// Creates a pop-up-menu that reflects the possible states of the specified
// meta-key.
static BPopUpMenu* CreateMetaPopUp(int col);
static BPopUpMenu* CreateMetaPopUp(int col)
{
MetaKeyStateMap& map = GetNthKeyMap(col);
BPopUpMenu * popup = new BPopUpMenu(NULL, false);
int numStates = map.GetNumStates();
for (int i = 0; i < numStates; i++)
popup->AddItem(new BMenuItem(map.GetNthStateDesc(i), NULL));
return popup;
}
// Creates a pop-up that allows the user to choose a key-cap visually
static BPopUpMenu* CreateKeysPopUp();
static BPopUpMenu* CreateKeysPopUp()
{
BPopUpMenu* popup = new BPopUpMenu(NULL, false);
int numKeys = GetNumKeyIndices();
for (int i = 0; i < numKeys; i++) {
const char* next = GetKeyName(i);
if (next)
popup->AddItem(new BMenuItem(next, NULL));
}
return popup;
}
ShortcutsWindow::ShortcutsWindow()
:
BWindow(BRect(WINDOW_START_X, WINDOW_START_Y, WINDOW_START_X + MIN_WIDTH,
WINDOW_START_Y + MIN_HEIGHT * 2), "Shortcuts", B_DOCUMENT_WINDOW, 0L),
fSavePanel(NULL),
fOpenPanel(NULL),
fSelectPanel(NULL),
fKeySetModified(false),
fLastOpenWasAppend(false)
{
InitializeMetaMaps();
SetSizeLimits(MIN_WIDTH, MAX_WIDTH, MIN_HEIGHT, MAX_HEIGHT);
BMenuBar* menuBar = new BMenuBar(BRect(0, 0, 0, 0), "Menu Bar");
BMenu* fileMenu = new BMenu("File");
fileMenu->AddItem(new BMenuItem("Open KeySet...",
new BMessage(OPEN_KEYSET), 'O'));
fileMenu->AddItem(new BMenuItem("Append KeySet...",
new BMessage(APPEND_KEYSET), 'A'));
fileMenu->AddItem(new BMenuItem("Revert to Saved",
new BMessage(REVERT_KEYSET), 'A'));
fileMenu->AddItem(new BSeparatorItem);
fileMenu->AddItem(new BMenuItem("Save KeySet As...",
new BMessage(SAVE_KEYSET_AS), 'S'));
fileMenu->AddItem(new BSeparatorItem);
fileMenu->AddItem(new BMenuItem("About Shortcuts", new BMessage(B_ABOUT_REQUESTED)));
fileMenu->AddItem(new BSeparatorItem);
fileMenu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED),
'Q'));
menuBar->AddItem(fileMenu);
AddChild(menuBar);
font_height fh;
be_plain_font->GetHeight(&fh);
float vButtonHeight = ceil(fh.ascent) + ceil(fh.descent) + 5.0f;
BRect tableBounds = Bounds();
tableBounds.top = menuBar->Bounds().bottom + 1;
tableBounds.right -= B_V_SCROLL_BAR_WIDTH;
tableBounds.bottom -= (B_H_SCROLL_BAR_HEIGHT + V_SPACING + vButtonHeight +
V_SPACING * 2);
BScrollView* containerView;
fColumnListView = new ColumnListView(tableBounds, &containerView, NULL,
B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE,
B_SINGLE_SELECTION_LIST, true, true, true, B_NO_BORDER);
fColumnListView->SetEditMessage(new BMessage(HOTKEY_ITEM_MODIFIED),
BMessenger(this));
const float metaWidth = 50.0f;
for (int i = 0; i < ShortcutsSpec::NUM_META_COLUMNS; i++)
fColumnListView->AddColumn(
new CLVColumn(ShortcutsSpec::GetColumnName(i), CreateMetaPopUp(i),
metaWidth, CLV_SORT_KEYABLE));
fColumnListView->AddColumn(new CLVColumn("Key", CreateKeysPopUp(), 60,
CLV_SORT_KEYABLE));
BPopUpMenu* popup = new BPopUpMenu(NULL, false);
popup->AddItem(new BMenuItem("(Choose App with File Requester)", NULL));
popup->AddItem(new BMenuItem("*InsertString \"Your Text Here\"", NULL));
popup->AddItem(new BMenuItem("*MoveMouse +20 +0", NULL));
popup->AddItem(new BMenuItem("*MoveMouseTo 50% 50%", NULL));
popup->AddItem(new BMenuItem("*MouseButton 1", NULL));
popup->AddItem(new BMenuItem("*LaunchHandler text/html", NULL));
popup->AddItem(new BMenuItem(
"*Multi \"*MoveMouseTo 100% 0\" \"*MouseButton 1\"", NULL));
popup->AddItem(new BMenuItem("*MouseDown", NULL));
popup->AddItem(new BMenuItem("*MouseUp", NULL));
popup->AddItem(new BMenuItem(
"*SendMessage application/x-vnd.Be-TRAK 'Tfnd'", NULL));
popup->AddItem(new BMenuItem("*Beep", NULL));
fColumnListView->AddColumn(new CLVColumn("Application", popup, 323.0,
CLV_SORT_KEYABLE));
fColumnListView->SetSortFunction(ShortcutsSpec::MyCompare);
AddChild(containerView);
fColumnListView->SetSelectionMessage(new BMessage(HOTKEY_ITEM_SELECTED));
fColumnListView->SetTarget(this);
BRect buttonBounds = Bounds();
buttonBounds.left += V_SPACING;
buttonBounds.right = ((buttonBounds.right - buttonBounds.left) / 2.0f) +
buttonBounds.left;
buttonBounds.bottom -= V_SPACING * 2;
buttonBounds.top = buttonBounds.bottom - vButtonHeight;
buttonBounds.right -= B_V_SCROLL_BAR_WIDTH;
float origRight = buttonBounds.right;
buttonBounds.right = (buttonBounds.left + origRight) * 0.40f -
(V_SPACING / 2);
AddChild(fAddButton = new ResizableButton(Bounds(), buttonBounds, "add",
"Add New Shortcut", new BMessage(ADD_HOTKEY_ITEM)));
buttonBounds.left = buttonBounds.right + V_SPACING;
buttonBounds.right = origRight;
AddChild(fRemoveButton = new ResizableButton(Bounds(), buttonBounds,
"remove", "Remove Selected Shortcut",
new BMessage(REMOVE_HOTKEY_ITEM)));
fRemoveButton->SetEnabled(false);
float offset = (buttonBounds.right - buttonBounds.left) / 2.0f;
BRect saveButtonBounds = buttonBounds;
saveButtonBounds.right = Bounds().right - B_V_SCROLL_BAR_WIDTH - offset;
saveButtonBounds.left = buttonBounds.right + V_SPACING + offset;
AddChild(fSaveButton = new ResizableButton(Bounds(), saveButtonBounds,
"save", "Save & Apply", new BMessage(SAVE_KEYSET)));
fSaveButton->SetEnabled(false);
entry_ref ref;
if (_GetSettingsFile(&ref)) {
BMessage msg(B_REFS_RECEIVED);
msg.AddRef("refs", &ref);
msg.AddString("startupRef", "please");
PostMessage(&msg); // Tell ourself to load this file if it exists.
}
Show();
}
ShortcutsWindow::~ShortcutsWindow()
{
delete fSavePanel;
delete fOpenPanel;
delete fSelectPanel;
be_app->PostMessage(B_QUIT_REQUESTED);
}
bool
ShortcutsWindow::QuitRequested()
{
bool ret = true;
if (fKeySetModified) {
BAlert* alert = new BAlert(WARNING,
"Really quit without saving your changes?", "Don't Save", "Cancel",
"Save");
switch(alert->Go()) {
case 1:
ret = false;
break;
case 2:
// Save: automatically if possible, otherwise go back and open
// up the file requester
if (fLastSaved.InitCheck() == B_NO_ERROR) {
if (_SaveKeySet(fLastSaved) == false) {
(new BAlert(ERROR,
"Shortcuts was unable to save your KeySet file!",
"Oh no"))->Go();
ret = true; //quit anyway
}
} else {
PostMessage(SAVE_KEYSET);
ret = false;
}
break;
default:
ret = true;
break;
}
}
if (ret)
fColumnListView->DeselectAll(); // avoid mysterious crash on PPC!?
return ret;
}
bool
ShortcutsWindow::_GetSettingsFile(entry_ref* eref)
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return false;
else
path.Append(SHORTCUTS_SETTING_FILE_NAME);
if (BEntry(path.Path(), true).GetRef(eref) == B_NO_ERROR)
return true;
else
return false;
}
// Saves a settings file to (saveEntry). Returns true iff successful.
bool
ShortcutsWindow::_SaveKeySet(BEntry& saveEntry)
{
BFile saveTo(&saveEntry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
if (saveTo.InitCheck() != B_NO_ERROR)
return false;
BMessage saveMsg;
for (int i = 0; i < fColumnListView->CountItems(); i++) {
BMessage next;
if (((ShortcutsSpec*)fColumnListView->ItemAt(i))->Archive(&next)
== B_NO_ERROR)
saveMsg.AddMessage("spec", &next);
else
printf("Error archiving ShortcutsSpec #%i!\n",i);
}
bool ret = (saveMsg.Flatten(&saveTo) == B_NO_ERROR);
if (ret) {
fKeySetModified = false;
fSaveButton->SetEnabled(false);
}
return ret;
}
// Appends new entries from the file specified in the "spec" entry of
// (loadMsg). Returns true iff successful.
bool
ShortcutsWindow::_LoadKeySet(const BMessage& loadMsg)
{
int i = 0;
BMessage msg;
while (loadMsg.FindMessage("spec", i++, &msg) == B_NO_ERROR) {
ShortcutsSpec* spec = (ShortcutsSpec*)ShortcutsSpec::Instantiate(&msg);
if (spec != NULL)
fColumnListView->AddItem(spec);
else
printf("_LoadKeySet: Error parsing spec!\n");
}
return true;
}
// Creates a new entry and adds it to the GUI. (defaultCommand) will be the
// text in the entry, or NULL if no text is desired.
void
ShortcutsWindow::_AddNewSpec(const char* defaultCommand)
{
_MarkKeySetModified();
ShortcutsSpec* spec;
int curSel = fColumnListView->CurrentSelection();
if (curSel >= 0) {
spec = new ShortcutsSpec(*((ShortcutsSpec*)
fColumnListView->ItemAt(curSel)));
if (defaultCommand)
spec->SetCommand(defaultCommand);
} else
spec = new ShortcutsSpec(defaultCommand ? defaultCommand : "");
fColumnListView->AddItem(spec);
fColumnListView->Select(fColumnListView->CountItems() - 1);
fColumnListView->ScrollToSelection();
}
void
ShortcutsWindow::MessageReceived(BMessage* msg)
{
switch(msg->what) {
case OPEN_KEYSET:
case APPEND_KEYSET:
fLastOpenWasAppend = (msg->what == APPEND_KEYSET);
if (fOpenPanel)
fOpenPanel->Show();
else {
BMessenger m(this);
fOpenPanel = new BFilePanel(B_OPEN_PANEL, &m, NULL, 0, false);
fOpenPanel->Show();
}
fOpenPanel->SetButtonLabel(B_DEFAULT_BUTTON, fLastOpenWasAppend ?
"Append" : "Open");
break;
case REVERT_KEYSET:
{
// Send a message to myself, to get me to reload the settings file
fLastOpenWasAppend = false;
BMessage reload(B_REFS_RECEIVED);
entry_ref eref;
_GetSettingsFile(&eref);
reload.AddRef("refs", &eref);
reload.AddString("startupRef", "yeah");
PostMessage(&reload);
}
break;
// Respond to drag-and-drop messages here
case B_SIMPLE_DATA:
{
int i = 0;
entry_ref ref;
while (msg->FindRef("refs", i++, &ref) == B_NO_ERROR) {
BEntry entry(&ref);
if (entry.InitCheck() == B_NO_ERROR) {
BPath path(&entry);
if (path.InitCheck() == B_NO_ERROR) {
// Add a new item with the given path.
BString str(path.Path());
DoStandardEscapes(str);
_AddNewSpec(str.String());
}
}
}
}
break;
// Respond to FileRequester's messages here
case B_REFS_RECEIVED:
{
// Find file ref
entry_ref ref;
bool isStartMsg = msg->HasString("startupRef");
if (msg->FindRef("refs", &ref) == B_NO_ERROR) {
// load the file into (fileMsg)
BMessage fileMsg;
{
BFile file(&ref, B_READ_ONLY);
if ((file.InitCheck() != B_NO_ERROR)
|| (fileMsg.Unflatten(&file) != B_NO_ERROR)) {
if (isStartMsg) {
// use this to save to anyway
fLastSaved = BEntry(&ref);
break;
} else {
(new BAlert(ERROR,
"Shortcuts was couldn't open your KeySet file!"
, "Okay"))->Go(NULL);
break;
}
}
}
if (fLastOpenWasAppend == false) {
// Clear the menu...
ShortcutsSpec * item;
do {
delete (item = ((ShortcutsSpec*)
fColumnListView->RemoveItem(int32(0))));
} while (item);
}
if (_LoadKeySet(fileMsg)) {
if (isStartMsg) fLastSaved = BEntry(&ref);
fSaveButton->SetEnabled(isStartMsg == false);
// If we just loaded in the Shortcuts settings file, then
// no need to tell the user to save on exit.
entry_ref eref;
_GetSettingsFile(&eref);
if (ref == eref) fKeySetModified = false;
} else {
(new BAlert(ERROR,
"Shortcuts was unable to parse your KeySet file!",
"Okay"))->Go(NULL);
break;
}
}
}
break;
// These messages come from the pop-up menu of the Applications column
case SELECT_APPLICATION:
{
int csel = fColumnListView->CurrentSelection();
if (csel >= 0) {
entry_ref aref;
if (msg->FindRef("refs", &aref) == B_NO_ERROR) {
BEntry ent(&aref);
if (ent.InitCheck() == B_NO_ERROR) {
BPath path;
if ((ent.GetPath(&path) == B_NO_ERROR)
&& (((ShortcutsSpec *)
fColumnListView->ItemAt(csel))->
ProcessColumnTextString(ShortcutsSpec::
STRING_COLUMN_INDEX, path.Path()))) {
fColumnListView->InvalidateItem(csel);
_MarkKeySetModified();
}
}
}
}
}
break;
case SAVE_KEYSET:
{
bool showSaveError = false;
const char * name;
entry_ref entry;
if ((msg->FindString("name", &name) == B_NO_ERROR)
&& (msg->FindRef("directory", &entry) == B_NO_ERROR)) {
BDirectory dir(&entry);
BEntry saveTo(&dir, name, true);
showSaveError = ((saveTo.InitCheck() != B_NO_ERROR)
|| (_SaveKeySet(saveTo) == false));
} else if (fLastSaved.InitCheck() == B_NO_ERROR) {
// We've saved this before, save over previous file.
showSaveError = (_SaveKeySet(fLastSaved) == false);
} else PostMessage(SAVE_KEYSET_AS); // open the save requester...
if (showSaveError) {
(new BAlert(ERROR, "Shortcuts wasn't able to save your keyset."
, "Okay"))->Go(NULL);
}
}
break;
case SAVE_KEYSET_AS:
{
if (fSavePanel)
fSavePanel->Show();
else {
BMessage msg(SAVE_KEYSET);
BMessenger messenger(this);
fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, NULL, 0,
false, &msg);
fSavePanel->Show();
}
}
break;
case B_ABOUT_REQUESTED:
be_app_messenger.SendMessage(B_ABOUT_REQUESTED);
break;
case ADD_HOTKEY_ITEM:
_AddNewSpec(NULL);
break;
case REMOVE_HOTKEY_ITEM:
{
int index = fColumnListView->CurrentSelection();
if (index >= 0) {
CLVListItem* item = (CLVListItem*)
fColumnListView->ItemAt(index);
fColumnListView->RemoveItem(index);
delete item;
_MarkKeySetModified();
// Rules for new selection: If there is an item at (index),
// select it. Otherwise, if there is an item at (index-1),
// select it. Otherwise, select nothing.
int num = fColumnListView->CountItems();
if (num > 0) {
if (index < num)
fColumnListView->Select(index);
else {
if (index > 0)
index--;
if (index < num)
fColumnListView->Select(index);
}
}
}
}
break;
// Received when the user clicks on the ColumnListView
case HOTKEY_ITEM_SELECTED:
{
int32 index = -1;
msg->FindInt32("index", &index);
bool validItem = (index >= 0);
fRemoveButton->SetEnabled(validItem);
}
break;
// Received when an entry is to be modified in response to GUI activity
case HOTKEY_ITEM_MODIFIED:
{
int32 row, column;
if ((msg->FindInt32("row", &row) == B_NO_ERROR)
&& (msg->FindInt32("column", &column) == B_NO_ERROR)) {
int32 key;
const char* bytes;
if (row >= 0) {
ShortcutsSpec* item = (ShortcutsSpec*)
fColumnListView->ItemAt(row);
bool repaintNeeded = false; // default
if (msg->HasInt32("mouseClick")) {
repaintNeeded = item->ProcessColumnMouseClick(column);
} else if ((msg->FindString("bytes", &bytes) == B_NO_ERROR)
&& (msg->FindInt32("key", &key) == B_NO_ERROR)) {
repaintNeeded = item->ProcessColumnKeyStroke(column,
bytes, key);
} else if (msg->FindInt32("unmappedkey", &key) ==
B_NO_ERROR) {
repaintNeeded = ((column == item->KEY_COLUMN_INDEX)
&& ((key > 0xFF) || (GetKeyName(key) != NULL))
&& (item->ProcessColumnKeyStroke(column, NULL,
key)));
} else if (msg->FindString("text", &bytes) == B_NO_ERROR) {
if ((bytes[0] == '(')&&(bytes[1] == 'C')) {
if (fSelectPanel)
fSelectPanel->Show();
else {
BMessage msg(SELECT_APPLICATION);
BMessenger m(this);
fSelectPanel = new BFilePanel(B_OPEN_PANEL, &m,
NULL, 0, false, &msg);
fSelectPanel->Show();
}
fSelectPanel->SetButtonLabel(B_DEFAULT_BUTTON,
"Select");
} else
repaintNeeded = item->ProcessColumnTextString(
column, bytes);
}
if (repaintNeeded) {
fColumnListView->InvalidateItem(row);
_MarkKeySetModified();
}
}
}
}
break;
default:
BWindow::MessageReceived(msg);
break;
}
}
void
ShortcutsWindow::_MarkKeySetModified()
{
if (fKeySetModified == false) {
fKeySetModified = true;
fSaveButton->SetEnabled(true);
}
}
void
ShortcutsWindow::Quit()
{
for (int i = fColumnListView->CountItems() - 1; i >= 0; i--)
delete (ShortcutsSpec*)fColumnListView->ItemAt(i);
fColumnListView->MakeEmpty();
BWindow::Quit();
}
void
ShortcutsWindow::FrameResized(float w, float h)
{
fAddButton->ChangeToNewSize(w, h);
fRemoveButton->ChangeToNewSize(w, h);
fSaveButton->ChangeToNewSize(w, h);
}
void
ShortcutsWindow::DispatchMessage(BMessage* msg, BHandler* handler)
{
switch(msg->what) {
case B_COPY:
case B_CUT:
if (be_clipboard->Lock()) {
int32 row = fColumnListView->CurrentSelection();
int32 column = fColumnListView->GetSelectedColumn();
if ((row >= 0)
&& (column == ShortcutsSpec::STRING_COLUMN_INDEX)) {
ShortcutsSpec* spec = (ShortcutsSpec*)
fColumnListView->ItemAt(row);
if (spec) {
BMessage* data = be_clipboard->Data();
data->RemoveName("text/plain");
data->AddData("text/plain", B_MIME_TYPE,
spec->GetCellText(column),
strlen(spec->GetCellText(column)));
be_clipboard->Commit();
if (msg->what == B_CUT) {
spec->ProcessColumnTextString(column, "");
_MarkKeySetModified();
fColumnListView->InvalidateItem(row);
}
}
}
be_clipboard->Unlock();
}
break;
case B_PASTE:
if (be_clipboard->Lock()) {
BMessage* data = be_clipboard->Data();
const char* text;
ssize_t textLen;
if (data->FindData("text/plain", B_MIME_TYPE, (const void**)
&text, &textLen) == B_NO_ERROR) {
int32 row = fColumnListView->CurrentSelection();
int32 column = fColumnListView->GetSelectedColumn();
if ((row >= 0)
&& (column == ShortcutsSpec::STRING_COLUMN_INDEX)) {
ShortcutsSpec* spec = (ShortcutsSpec*)
fColumnListView->ItemAt(row);
if (spec) {
for (ssize_t i = 0; i < textLen; i++) {
char buf[2] = {text[i], 0x00};
spec->ProcessColumnKeyStroke(column, buf, 0);
}
}
fColumnListView->InvalidateItem(row);
_MarkKeySetModified();
}
}
be_clipboard->Unlock();
}
break;
default:
BWindow::DispatchMessage(msg, handler);
break;
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
*/
#ifndef ShortcutsWindow_h
#define ShortcutsWindow_h
#include <Message.h>
#include <Window.h>
#include <Point.h>
#include <Entry.h>
#include <FilePanel.h>
#include "ColumnListView.h"
#include "ResizableButton.h"
// This class defines our preferences/configuration window.
class ShortcutsWindow : public BWindow {
public:
// If (optSaveTo) is non-NULL, settings will be loaded from the file it
// represents.
ShortcutsWindow();
~ShortcutsWindow();
virtual void DispatchMessage(BMessage* msg, BHandler* handler);
virtual void Quit();
virtual void FrameResized(float w, float h);
virtual void MessageReceived(BMessage * msg);
virtual bool QuitRequested();
// BMessage 'what' codes, representing commands understood by this Window.
enum {
ADD_HOTKEY_ITEM = 'SpKy', // Add a new hotkey entry to the GUI list.
REMOVE_HOTKEY_ITEM, // Remove a hotkey entry from the GUI list.
HOTKEY_ITEM_SELECTED, // Give the "focus bar" to the specified
// entry.
HOTKEY_ITEM_MODIFIED, // Update the state of an entry to reflect
// user's changes.
OPEN_KEYSET, // Bring up a File requester to load new
// settings.
APPEND_KEYSET, // Bring up a File requester to append
// settings.
REVERT_KEYSET, // Dump the current state and re-read
// settings from disk.
SAVE_KEYSET, // Save the current settings to disk
SAVE_KEYSET_AS, // Bring up a File requester to save
// current settings.
SELECT_APPLICATION, // Set the current entry to point to the
// given file.
};
private:
BMenuItem* _CreateActuatorPresetMenuItem(const char* label)
const;
void _AddNewSpec(const char* defaultCommand);
void _MarkKeySetModified();
bool _LoadKeySet(const BMessage& loadMsg);
bool _SaveKeySet(BEntry & saveEntry);
bool _GetSettingsFile(entry_ref* ref);
ResizableButton* fAddButton;
ResizableButton* fRemoveButton;
ResizableButton* fSaveButton;
ColumnListView* fColumnListView;
BFilePanel* fSavePanel; // for saving settings
BFilePanel* fOpenPanel; // for loading settings
BFilePanel* fSelectPanel; // for selecting apps to launch
// Points to the settings file to save to
BEntry fLastSaved;
// true iff changes were made since last load or save
bool fKeySetModified;
// true iff the file-requester's ref should be appended to current
bool fLastOpenWasAppend;
};
#endif
+217
View File
@@ -0,0 +1,217 @@
//Column list header source file
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#define CLVColumn_CPP
#include <string.h>
#include "CLVColumn.h"
#include "ColumnListView.h"
#include "CLVColumnLabelView.h"
//******************************************************************************************************
//**** CLVColumn CLASS DEFINITION
//******************************************************************************************************
CLVColumn::CLVColumn(const char* label,BPopUpMenu * popup,float width,uint32 flags,float min_width)
{
fPopup = popup;
if(flags & CLV_EXPANDER)
{
label = NULL;
width = 20.0;
min_width = 20.0;
flags &= CLV_NOT_MOVABLE | CLV_LOCK_AT_BEGINNING | CLV_HIDDEN | CLV_LOCK_WITH_RIGHT;
flags |= CLV_EXPANDER | CLV_NOT_RESIZABLE | CLV_MERGE_WITH_RIGHT;
}
if(min_width < 4.0)
min_width = 4.0;
if(width < min_width)
width = min_width;
if(label)
{
char* Temp = new char[strlen(label)+1];
strcpy(Temp,label);
label = Temp;
}
if(label)
{
fLabel = new char[strlen(label)+1];
strcpy((char*)fLabel,label);
}
else
fLabel = NULL;
fWidth = width;
fMinWidth = min_width;
fFlags = flags;
fPushedByExpander = false;
fParent = NULL;
fSortMode = NoSort;
}
CLVColumn::~CLVColumn()
{
if(fLabel) delete[] fLabel;
if(fParent) fParent->RemoveColumn(this);
delete fPopup;
}
float CLVColumn::Width() const
{
return fWidth;
}
void CLVColumn::SetWidth(float width)
{
if(width < fMinWidth)
width = fMinWidth;
if(width != fWidth)
{
float OldWidth = fWidth;
fWidth = width;
if(IsShown() && fParent)
{
BWindow* ParentWindow = fParent->Window();
if(ParentWindow)
ParentWindow->Lock();
//Figure out the area after this column to scroll
BRect ColumnViewBounds = fParent->fColumnLabelView->Bounds();
BRect MainViewBounds = fParent->Bounds();
BRect SourceArea = ColumnViewBounds;
SourceArea.left = fColumnEnd+1.0;
BRect DestArea = SourceArea;
float Delta = width-OldWidth;
DestArea.left += Delta;
DestArea.right += Delta;
float LimitShift;
if(DestArea.right > ColumnViewBounds.right)
{
LimitShift = DestArea.right-ColumnViewBounds.right;
DestArea.right -= LimitShift;
SourceArea.right -= LimitShift;
}
if(DestArea.left < ColumnViewBounds.left)
{
LimitShift = ColumnViewBounds.left - DestArea.left;
DestArea.left += LimitShift;
SourceArea.left += LimitShift;
}
//Scroll the area that is being shifted
if(ParentWindow)
ParentWindow->UpdateIfNeeded();
fParent->fColumnLabelView->CopyBits(SourceArea,DestArea);
SourceArea.top = MainViewBounds.top;
SourceArea.bottom = MainViewBounds.bottom;
DestArea.top = MainViewBounds.top;
DestArea.bottom = MainViewBounds.bottom;
fParent->CopyBits(SourceArea,DestArea);
//Invalidate the region that got revealed
DestArea = ColumnViewBounds;
if(width > OldWidth)
{
DestArea.left = fColumnEnd+1.0;
DestArea.right = fColumnEnd+Delta;
}
else
{
DestArea.left = ColumnViewBounds.right+Delta+1.0;
DestArea.right = ColumnViewBounds.right;
}
fParent->fColumnLabelView->Invalidate(DestArea);
DestArea.top = MainViewBounds.top;
DestArea.bottom = MainViewBounds.bottom;
fParent->Invalidate(DestArea);
//Invalidate the old or new resize handle as necessary
DestArea = ColumnViewBounds;
if(width > OldWidth)
DestArea.left = fColumnEnd;
else
DestArea.left = fColumnEnd + Delta;
DestArea.right = DestArea.left;
fParent->fColumnLabelView->Invalidate(DestArea);
//Update the column sizes, positions and group positions
fParent->UpdateColumnSizesDataRectSizeScrollBars();
fParent->fColumnLabelView->UpdateDragGroups();
if(ParentWindow)
ParentWindow->Unlock();
}
if(fParent)
fParent->ColumnWidthChanged(fParent->fColumnList.IndexOf(this),fWidth);
}
}
uint32 CLVColumn::Flags() const
{
return fFlags;
}
bool CLVColumn::IsShown() const
{
if(fFlags & CLV_HIDDEN)
return false;
else
return true;
}
void CLVColumn::SetShown(bool Shown)
{
bool shown = IsShown();
if(shown != Shown)
{
if(Shown)
fFlags &= 0xFFFFFFFF^CLV_HIDDEN;
else
fFlags |= CLV_HIDDEN;
if(fParent)
{
float UpdateLeft = fColumnBegin;
BWindow* ParentWindow = fParent->Window();
if(ParentWindow)
ParentWindow->Lock();
fParent->UpdateColumnSizesDataRectSizeScrollBars();
fParent->fColumnLabelView->UpdateDragGroups();
if(Shown)
UpdateLeft = fColumnBegin;
BRect Area = fParent->fColumnLabelView->Bounds();
Area.left = UpdateLeft;
fParent->fColumnLabelView->Invalidate(Area);
Area = fParent->Bounds();
Area.left = UpdateLeft;
fParent->Invalidate(Area);
if(fFlags & CLV_EXPANDER)
{
if(!Shown)
fParent->fExpanderColumn = -1;
else
fParent->fExpanderColumn = fParent->IndexOfColumn(this);
}
if(ParentWindow)
ParentWindow->Unlock();
}
}
}
CLVSortMode CLVColumn::SortMode() const
{
return fSortMode;
}
void CLVColumn::SetSortMode(CLVSortMode mode)
{
if(fParent)
fParent->SetSortMode(fParent->IndexOfColumn(this),mode);
else
fSortMode = mode;
}
+107
View File
@@ -0,0 +1,107 @@
#ifndef CLVColumn_h
#define CLVColumn_h
#include <support/SupportDefs.h>
#include <interface/PopUpMenu.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
class ColumnListView;
class CLVColumn;
class CLVListItem;
//******************************************************************************************************
//**** CONSTANTS
//******************************************************************************************************
//Flags
enum
{
CLV_SORT_KEYABLE = 0x00000001, //Can be used as the sorting key
CLV_NOT_MOVABLE = 0x00000002, //Column can't be moved by user
CLV_NOT_RESIZABLE = 0x00000004, //Column can't be resized by user
CLV_LOCK_AT_BEGINNING = 0x00000008, //Movable columns may not be placed or moved by the user
//into a position before this one
CLV_LOCK_AT_END = 0x00000010, //Movable columns may not be placed or moved by the user
//into a position after this one
CLV_HIDDEN = 0x00000020, //This column is hidden initially
CLV_MERGE_WITH_RIGHT = 0x00000040, //Merge this column label with the one that follows it.
CLV_LOCK_WITH_RIGHT = 0x00000080, //Lock this column to the one that follows it such that
//if the column to the right is moved by the user, this
//one will move with it and vice versa
CLV_EXPANDER = 0x00000100, //Column contains an expander. You may only use one
//expander in a ColumnListView, and an expander may not be
//added to a non-hierarchal ColumnListView. It may not
//have a label. Its width is automatically set to 20.0.
//The only flags that affect it are CLV_NOT_MOVABLE,
//CLV_LOCK_AT_BEGINNING, CLV_NOT_SHOWN and
//CLV_LOCK_WITH_RIGHT. The others are set for you:
//CLV_NOT_RESIZABLE | CLV_MERGE_WITH_RIGHT
CLV_PUSH_PASS = 0x00000200 //Causes this column, if pushed by an expander to the
//left, to pass that push on and also push the next
}; //column to the right.
enum CLVSortMode
{
Ascending,
Descending,
NoSort
};
//******************************************************************************************************
//**** ColumnListView CLASS DECLARATION
//******************************************************************************************************
class CLVColumn
{
public:
//Constructor and destructor
CLVColumn( const char* label,
BPopUpMenu * popup = NULL,
float width = 20.0,
uint32 flags = 0L,
float min_width = 20.0);
virtual ~CLVColumn();
//Archival stuff
/* Not implemented yet
CLVColumn(BMessage* archive);
static CLVColumn* Instantiate(BMessage* data);
virtual status_t Archive(BMessage* data, bool deep = true) const;
*/
//Functions
float Width() const;
virtual void SetWidth(float width); //Can be overridden to detect changes to the column width
//however since you are probably overriding
//ColumnListView and dealing with an array of columns
//anyway, it is probably more useful to override
//ColumnListView::ColumnWidthChanged to detect changes to
//column widths
uint32 Flags() const;
bool IsShown() const;
void SetShown(bool shown);
CLVSortMode SortMode() const;
void SetSortMode(CLVSortMode mode);
const char * GetLabel() const {return fLabel;}
BPopUpMenu * GetPopup() {return fPopup;}
private:
friend class ColumnListView;
friend class CLVColumnLabelView;
friend class CLVListItem;
const char *fLabel;
float fWidth;
float fMinWidth;
float fColumnBegin;
float fColumnEnd;
uint32 fFlags;
bool fPushedByExpander;
CLVSortMode fSortMode;
ColumnListView* fParent;
BPopUpMenu * fPopup; // added by jaf
};
#endif
@@ -0,0 +1,756 @@
//ColumnLabelView class source file
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#define CLVColumnLabelView_CPP
#include "CLVColumnLabelView.h"
#include "ColumnListView.h"
#include "CLVColumn.h"
#include "MouseWatcher.h"
//******************************************************************************************************
//**** FUNCTION DEFINITIONS
//******************************************************************************************************
CLVColumnLabelView::CLVColumnLabelView(BRect Bounds,ColumnListView* parent,const BFont* Font)
: BView(Bounds,NULL,B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP,B_WILL_DRAW|B_FRAME_EVENTS),
fDragGroups(10)
{
SetFont(Font);
SetViewColor(BeBackgroundGrey);
SetLowColor(BeBackgroundGrey);
SetHighColor(Black);
fParent = parent;
fDisplayList = &fParent->fColumnDisplayList;
fColumnClicked = NULL;
fColumnDragging = false;
fColumnResizing = false;
font_height FontAttributes;
Font->GetHeight(&FontAttributes);
fFontAscent = ceil(FontAttributes.ascent);
}
CLVColumnLabelView::~CLVColumnLabelView()
{
int32 NumberOfGroups = fDragGroups.CountItems();
for(int32 Counter = 0; Counter < NumberOfGroups; Counter++)
fDragGroups.RemoveItem(int32(0));
}
void CLVColumnLabelView::Draw(BRect UpdateRect)
{
BRegion ClippingRegion;
GetClippingRegion(&ClippingRegion);
BRect ViewBounds = Bounds();
//Draw each column label in turn
float ColumnBegin = 0.0;
float ColumnEnd = -1.0;
bool MergeWithLeft = false;
int32 NumberOfColumns = fDisplayList->CountItems();
BPoint Start,Stop;
for(int32 ColumnDraw = 0; ColumnDraw < NumberOfColumns; ColumnDraw++)
{
CLVColumn* ThisColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnDraw);
if(ThisColumn->IsShown())
{
//Figure out where this column is
ColumnBegin = ThisColumn->fColumnBegin;
ColumnEnd = ThisColumn->fColumnEnd;
//Start by figuring out if this column will merge with a shown column to the right
bool MergeWithRight = false;
if(ThisColumn->fFlags & CLV_MERGE_WITH_RIGHT)
{
for(int32 ColumnCounter = ColumnDraw+1; ColumnCounter < NumberOfColumns;
ColumnCounter++)
{
CLVColumn* NextColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnCounter);
if(NextColumn->IsShown())
{
//The next column is shown
MergeWithRight = true;
break;
}
else if(!(NextColumn->fFlags & CLV_MERGE_WITH_RIGHT))
//The next column is not shown and doesn't pass on the merge
break;
}
}
if(ClippingRegion.Intersects(BRect(ColumnBegin,ViewBounds.top,ColumnEnd,
ViewBounds.bottom)))
{
//Need to draw this column
BeginLineArray(4);
//Top line
Start.Set(ColumnBegin,ViewBounds.top);
Stop.Set(ColumnEnd-1.0,ViewBounds.top);
if(MergeWithRight && !(ThisColumn == fColumnClicked && fColumnResizing))
Stop.x = ColumnEnd;
AddLine(Start,Stop,BeHighlight);
//Left line
if(!MergeWithLeft)
AddLine(BPoint(ColumnBegin,ViewBounds.top+1.0),
BPoint(ColumnBegin,ViewBounds.bottom),BeHighlight);
//Bottom line
Start.Set(ColumnBegin+1.0,ViewBounds.bottom);
if(MergeWithLeft)
Start.x = ColumnBegin;
Stop.Set(ColumnEnd-1.0,ViewBounds.bottom);
if(MergeWithRight && !(ThisColumn == fColumnClicked && fColumnResizing))
Stop.x = ColumnEnd;
AddLine(Start,Stop,BeShadow);
//Right line
if(ThisColumn == fColumnClicked && fColumnResizing)
AddLine(BPoint(ColumnEnd,ViewBounds.top),BPoint(ColumnEnd,ViewBounds.bottom),
BeFocusBlue);
else if(!MergeWithRight)
AddLine(BPoint(ColumnEnd,ViewBounds.top),BPoint(ColumnEnd,ViewBounds.bottom),
BeShadow);
EndLineArray();
//Add the label
if(ThisColumn->fLabel)
{
//Limit the clipping region to the interior of the box
BRegion TextRegion;
TextRegion.Include(BRect(ColumnBegin+1.0,ViewBounds.top+1.0,ColumnEnd-1.0,
ViewBounds.bottom-1.0));
ConstrainClippingRegion(&TextRegion);
//Draw the label
if(ThisColumn == fColumnClicked && !fColumnResizing)
SetHighColor(BeFocusBlue);
SetDrawingMode(B_OP_OVER);
DrawString(ThisColumn->fLabel,BPoint(ColumnBegin+9.0,ViewBounds.top+2.0+fFontAscent));
SetDrawingMode(B_OP_COPY);
//Underline if this is a selected sort column
if(fParent->fSortKeyList.HasItem(ThisColumn) && ThisColumn->fSortMode != NoSort)
{
float Width = StringWidth(ThisColumn->fLabel);
StrokeLine(BPoint(ColumnBegin+8.0,ViewBounds.top+2.0+fFontAscent+2.0),
BPoint(ColumnBegin+8.0+Width,ViewBounds.top+2.0+fFontAscent+2.0));
}
if(ThisColumn == fColumnClicked && !fColumnResizing)
SetHighColor(Black);
//Restore the clipping region
ConstrainClippingRegion(NULL);
}
}
//Set MergeWithLeft flag for the next column to the appropriate state
MergeWithLeft = MergeWithRight;
}
}
//Add highlight and shadow to the region after the columns if necessary
if(ColumnEnd < ViewBounds.right)
{
ColumnBegin = ColumnEnd+1.0;
if(ClippingRegion.Intersects(BRect(ColumnEnd+1.0,ViewBounds.top,ViewBounds.right,
ViewBounds.bottom)))
{
BeginLineArray(3);
//Top line
AddLine(BPoint(ColumnBegin,ViewBounds.top),BPoint(ViewBounds.right,ViewBounds.top),
BeHighlight);
//Left line
AddLine(BPoint(ColumnBegin,ViewBounds.top+1.0),BPoint(ColumnBegin,ViewBounds.bottom),
BeHighlight);
//Bottom line
Start.Set(ColumnBegin+1.0,ViewBounds.bottom);
if(MergeWithLeft)
Start.x = ColumnBegin;
Stop.Set(ViewBounds.right,ViewBounds.bottom);
AddLine(Start,Stop,BeShadow);
EndLineArray();
}
}
//Draw the dragging box if necessary
if(fColumnClicked && fColumnDragging)
{
float DragOutlineLeft = fPreviousMousePos.x-fDragBoxMouseHoldOffset;
float GroupBegin = ((CLVDragGroup*)fDragGroups.ItemAt(fDragGroup))->GroupBegin;
if(DragOutlineLeft < GroupBegin && fSnapGroupBefore == -1)
DragOutlineLeft = GroupBegin;
if(DragOutlineLeft > GroupBegin && fSnapGroupAfter == -1)
DragOutlineLeft = GroupBegin;
float DragOutlineRight = DragOutlineLeft + fDragBoxWidth;
BeginLineArray(4);
AddLine(BPoint(DragOutlineLeft,ViewBounds.top),BPoint(DragOutlineRight,
ViewBounds.top),BeFocusBlue);
AddLine(BPoint(DragOutlineLeft,ViewBounds.bottom),BPoint(DragOutlineRight,
ViewBounds.bottom),BeFocusBlue);
AddLine(BPoint(DragOutlineLeft,ViewBounds.top+1.0),BPoint(DragOutlineLeft,
ViewBounds.bottom-1.0),BeFocusBlue);
AddLine(BPoint(DragOutlineRight,ViewBounds.top+1.0),BPoint(DragOutlineRight,
ViewBounds.bottom-1.0),BeFocusBlue);
EndLineArray();
fPrevDragOutlineLeft = DragOutlineLeft;
fPrevDragOutlineRight = DragOutlineRight;
}
}
void CLVColumnLabelView::MouseDown(BPoint Point)
{
//Only pay attention to primary mouse button
bool WatchMouse = false;
BPoint MousePos;
uint32 Buttons;
GetMouse(&MousePos,&Buttons);
if(Buttons == B_PRIMARY_MOUSE_BUTTON)
{
BRect ViewBounds = Bounds();
//Make sure no other column was already clicked. If so, just discard the old one and redraw the
//view
if(fColumnClicked != NULL)
{
Invalidate();
fColumnClicked = NULL;
}
//Find the column that the user clicked, if any
bool GrabbedResizeTab = false;
int32 NumberOfColumns = fDisplayList->CountItems();
int32 ColumnFind;
CLVColumn* ThisColumn;
for(ColumnFind = 0; ColumnFind < NumberOfColumns; ColumnFind++)
{
ThisColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
if(ThisColumn->IsShown())
{
float ColumnBegin = ThisColumn->fColumnBegin;
float ColumnEnd = ThisColumn->fColumnEnd;
if ((Point.x >= ColumnBegin && Point.x <= ColumnEnd) ||
((ColumnFind == NumberOfColumns-1)&&(Point.x >= ColumnBegin))) // anything after the rightmost column can drag... jaf
{
const float resizeTolerance = 5.0f; // jaf is too clumsy to click on a 2 pixel space. :)
//User clicked in this column
if(Point.x <= ColumnBegin+resizeTolerance)
{
//User clicked the resize tab preceding this column
for(ColumnFind--; ColumnFind >= 0; ColumnFind--)
{
ThisColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
if(ThisColumn->IsShown())
{
GrabbedResizeTab = true;
break;
}
}
}
else if(Point.x >= ColumnEnd-resizeTolerance)
{
//User clicked the resize tab for (after) this column
GrabbedResizeTab = true;
}
else
{
//The user clicked in this column
fColumnClicked = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
fColumnResizing = false;
fPreviousMousePos = Point;
fMouseClickedPos = Point;
fColumnDragging = false;
SetSnapMinMax();
fDragBoxMouseHoldOffset = Point.x-
((CLVDragGroup*)fDragGroups.ItemAt(fDragGroup))->GroupBegin;
Invalidate(BRect(ColumnBegin+1.0,ViewBounds.top+1.0,ColumnEnd-1.0,
ViewBounds.bottom-1.0));
//Start watching the mouse
WatchMouse = true;
}
break;
}
}
}
if(GrabbedResizeTab)
{
//The user grabbed a resize tab. See if resizing of this column is allowed
if(!(ThisColumn->fFlags & CLV_NOT_RESIZABLE))
{
fColumnClicked = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
fColumnResizing = true;
fPreviousMousePos = Point;
fMouseClickedPos = Point;
fColumnDragging = false;
fResizeMouseHoldOffset = Point.x-fColumnClicked->fColumnEnd;
Invalidate(BRect(fColumnClicked->fColumnEnd,ViewBounds.top,ThisColumn->fColumnEnd,
ViewBounds.bottom));
//Start watching the mouse
WatchMouse = true;
}
}
}
if(WatchMouse)
{
thread_id MouseWatcherThread = StartMouseWatcher(this);
if(MouseWatcherThread == B_NO_MORE_THREADS || MouseWatcherThread == B_NO_MEMORY)
fColumnClicked = NULL;
}
}
void CLVColumnLabelView::MessageReceived(BMessage *message)
{
if(message->what != MW_MOUSE_MOVED && message->what != MW_MOUSE_DOWN && message->what != MW_MOUSE_UP)
BView::MessageReceived(message);
else if(fColumnClicked != NULL)
{
BPoint MousePos;
message->FindPoint("where",&MousePos);
uint32 Buttons;
message->FindInt32("buttons",(int32*)&Buttons);
uint32 Modifiers;
message->FindInt32("modifiers",(int32*)&Modifiers);
BRect ViewBounds;
ViewBounds = Bounds();
uint32 ColumnFlags = fColumnClicked->Flags();
if(Buttons == B_PRIMARY_MOUSE_BUTTON)
{
//Mouse is still held down
if(!fColumnResizing)
{
//User is clicking or dragging
if((MousePos.x<fMouseClickedPos.x-2.0 || MousePos.x>fMouseClickedPos.x+2.0) &&
!fColumnDragging)
{
//User is initiating a drag
if(fTheDragGroup->Flags & CLV_NOT_MOVABLE)
{
//Not allowed to drag this column - terminate the click
Invalidate(BRect(fColumnClicked->fColumnBegin,ViewBounds.top,
fColumnClicked->fColumnEnd,ViewBounds.bottom));
fColumnClicked = NULL;
}
else
{
//Actually initiate a drag
fColumnDragging = true;
fPrevDragOutlineLeft = -1.0;
fPrevDragOutlineRight = -1.0;
}
}
//Now deal with dragging
if(fColumnDragging)
{
//User is dragging
if(MousePos.x<fPreviousMousePos.x || MousePos.x>fPreviousMousePos.x)
{
//Mouse moved since I last checked
ViewBounds = Bounds();
bool ColumnSnapped;
do
{
//Live dragging of columns
ColumnSnapped = false;
float ColumnsUpdateLeft,ColumnsUpdateRight;
float MainViewUpdateLeft,MainViewUpdateRight;
CLVColumn* LastSwapColumn;
if(fSnapMin != -1.0 && MousePos.x < fSnapMin)
{
//Shift the group left
ColumnsUpdateLeft = fTheShownGroupBefore->GroupBegin;
ColumnsUpdateRight = fTheDragGroup->GroupEnd;
MainViewUpdateLeft = ColumnsUpdateLeft;
MainViewUpdateRight = ColumnsUpdateRight;
LastSwapColumn = fTheShownGroupBefore->LastColumnShown;
if(fTheDragGroup->LastColumnShown->fFlags & CLV_MERGE_WITH_RIGHT)
ColumnsUpdateRight += 1.0;
else if(fTheShownGroupBefore->LastColumnShown->fFlags & CLV_MERGE_WITH_RIGHT)
ColumnsUpdateRight += 1.0;
ShiftDragGroup(fSnapGroupBefore);
ColumnSnapped = true;
}
if(fSnapMax != -1.0 && MousePos.x > fSnapMax)
{
//Shift the group right
ColumnsUpdateLeft = fTheDragGroup->GroupBegin;
ColumnsUpdateRight = fTheShownGroupAfter->GroupEnd;
MainViewUpdateLeft = ColumnsUpdateLeft;
MainViewUpdateRight = ColumnsUpdateRight;
LastSwapColumn = fTheDragGroup->LastColumnShown;
if(fTheDragGroup->LastColumnShown->fFlags & CLV_MERGE_WITH_RIGHT)
ColumnsUpdateRight += 1.0;
else if(fTheShownGroupAfter->LastColumnShown->fFlags & CLV_MERGE_WITH_RIGHT)
ColumnsUpdateRight += 1.0;
ShiftDragGroup(fSnapGroupAfter+1);
ColumnSnapped = true;
}
if(ColumnSnapped)
{
//Redraw the snapped column labels
Invalidate(BRect(ColumnsUpdateLeft,ViewBounds.top,ColumnsUpdateRight,
ViewBounds.bottom));
BRect MainViewBounds = fParent->Bounds();
//Modify MainViewUpdateRight if more columns are pushed by expanders
if(LastSwapColumn->fFlags & CLV_EXPANDER ||
(LastSwapColumn->fPushedByExpander && (LastSwapColumn->fFlags &
CLV_PUSH_PASS)))
{
int32 NumberOfColumns = fDisplayList->CountItems();
for(int32 ColumnCounter = fDisplayList->IndexOf(LastSwapColumn)+1;
ColumnCounter < NumberOfColumns; ColumnCounter++)
{
CLVColumn* ThisColumn =
(CLVColumn*)fDisplayList->ItemAt(ColumnCounter);
if(ThisColumn->IsShown())
{
if(ThisColumn->fPushedByExpander)
MainViewUpdateRight = ThisColumn->fColumnEnd;
else
break;
}
}
}
fParent->Invalidate(BRect(MainViewUpdateLeft,MainViewBounds.top,
MainViewUpdateRight,MainViewBounds.bottom));
}
}while(ColumnSnapped);
//Erase and redraw the drag rectangle but not the interior to avoid label flicker
float Min = fPrevDragOutlineLeft;
float Max = fPrevDragOutlineRight;
float Min2 = MousePos.x-fDragBoxMouseHoldOffset;
float GroupBegin = ((CLVDragGroup*)fDragGroups.ItemAt(fDragGroup))->GroupBegin;
if(Min2 < GroupBegin && fSnapGroupBefore == -1)
Min2 = GroupBegin;
if(Min2 > GroupBegin && fSnapGroupAfter == -1)
Min2 = GroupBegin;
float Max2 = Min2 + fDragBoxWidth;
float Temp;
if(Min2 < Min || Min == -1.0)
{Temp = Min2;Min2 = Min;Min = Temp;}
if(Max2 > Max || Max == -1.0)
{Temp = Max2;Max2 = Max;Max = Temp;}
Invalidate(BRect(Min,ViewBounds.top+1.0,Min,ViewBounds.bottom-1.0));
if(Min2 != -1.0)
Invalidate(BRect(Min2,ViewBounds.top+1.0,Min2,ViewBounds.bottom-1.0));
Invalidate(BRect(Max,ViewBounds.top+1.0,Max,ViewBounds.bottom-1.0));
if(Max2 != -1.0)
Invalidate(BRect(Max2,ViewBounds.top+1.0,Max2,ViewBounds.bottom-1.0));
Invalidate(BRect(Min,ViewBounds.top,Max,ViewBounds.top));
Invalidate(BRect(Min,ViewBounds.bottom,Max,ViewBounds.bottom));
}
}
}
else
{
//User is resizing the column
if(MousePos.x<fPreviousMousePos.x || MousePos.x>fPreviousMousePos.x)
{
float NewWidth = MousePos.x - fResizeMouseHoldOffset - fColumnClicked->fColumnBegin;
if(NewWidth < fColumnClicked->fMinWidth)
NewWidth = fColumnClicked->fMinWidth;
if(NewWidth != fColumnClicked->fWidth)
{
fColumnClicked->SetWidth(NewWidth);
fParent->ColumnWidthChanged(fParent->IndexOfColumn(fColumnClicked),NewWidth);
}
}
}
}
else if(Buttons == 0)
{
//Mouse button was released
if(!fColumnDragging && !fColumnResizing)
{
//Column was clicked
if(ColumnFlags&CLV_SORT_KEYABLE)
{
//The column is a "sortable" column
if(!(Modifiers&B_SHIFT_KEY))
{
//The user wants to select it as the main sorting column
if(fParent->fSortKeyList.ItemAt(0) == fColumnClicked)
//The column was already selected; switch sort modes
fParent->ReverseSortMode(fParent->fColumnList.IndexOf(fColumnClicked));
else
//The user selected this column for sorting
fParent->SetSortKey(fParent->fColumnList.IndexOf(fColumnClicked));
}
else
{
//The user wants to add it as a secondary sorting column
if(fParent->fSortKeyList.HasItem(fColumnClicked))
//The column was already selected; switch sort modes
fParent->ReverseSortMode(fParent->fColumnList.IndexOf(fColumnClicked));
else
//The user selected this column for sorting
fParent->AddSortKey(fParent->fColumnList.IndexOf(fColumnClicked));
}
}
}
else if(fColumnDragging)
{
//Column was dragging; erase the drag box but not the interior to avoid label flicker
Invalidate(BRect(fPrevDragOutlineLeft,ViewBounds.top+1.0,
fPrevDragOutlineLeft,ViewBounds.bottom-1.0));
Invalidate(BRect(fPrevDragOutlineRight,ViewBounds.top+1.0,
fPrevDragOutlineRight,ViewBounds.bottom-1.0));
Invalidate(BRect(fPrevDragOutlineLeft,ViewBounds.top,
fPrevDragOutlineRight,ViewBounds.top));
Invalidate(BRect(fPrevDragOutlineLeft,ViewBounds.bottom,
fPrevDragOutlineRight,ViewBounds.bottom));
}
else
//Column was resizing; erase the drag tab
Invalidate(BRect(fColumnClicked->fColumnEnd,ViewBounds.top,fColumnClicked->fColumnEnd,
ViewBounds.bottom));
//Unhighlight the label and forget the column
Invalidate(BRect(fColumnClicked->fColumnBegin+1.0,ViewBounds.top+1.0,
fColumnClicked->fColumnEnd-1.0,ViewBounds.bottom-1.0));
fColumnClicked = NULL;
fColumnDragging = false;
fColumnResizing = false;
}
else
{
//Unused button combination
//Unhighlight the label and forget the column
Invalidate(BRect(fColumnClicked->fColumnBegin+1.0,ViewBounds.top+1.0,
fColumnClicked->fColumnEnd-1.0,ViewBounds.bottom-1.0));
fColumnClicked = NULL;
fColumnDragging = false;
fColumnResizing = false;
}
fPreviousMousePos = MousePos;
}
}
void CLVColumnLabelView::ShiftDragGroup(int32 NewPos)
//Shift the drag group into a new position
{
int32 NumberOfGroups = fDragGroups.CountItems();
int32 GroupCounter;
CLVDragGroup* ThisGroup;
int32 NumberOfColumnsInGroup;
int32 ColumnCounter;
BList NewDisplayList;
//Copy the groups up to the new position
for(GroupCounter = 0; GroupCounter < NewPos; GroupCounter++)
{
if(GroupCounter != fDragGroup)
{
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(GroupCounter);
NumberOfColumnsInGroup = ThisGroup->GroupStopDispListIndex -
ThisGroup->GroupStartDispListIndex + 1;
for(ColumnCounter = ThisGroup->GroupStartDispListIndex; ColumnCounter <=
ThisGroup->GroupStopDispListIndex; ColumnCounter++)
NewDisplayList.AddItem(fDisplayList->ItemAt(ColumnCounter));
}
}
//Copy the group into the new position
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(fDragGroup);
NumberOfColumnsInGroup = ThisGroup->GroupStopDispListIndex - ThisGroup->GroupStartDispListIndex + 1;
for(ColumnCounter = ThisGroup->GroupStartDispListIndex; ColumnCounter <=
ThisGroup->GroupStopDispListIndex; ColumnCounter++)
NewDisplayList.AddItem(fDisplayList->ItemAt(ColumnCounter));
//Copy the rest of the groups, but skip the dragging group
for(GroupCounter = NewPos; GroupCounter < NumberOfGroups; GroupCounter++)
{
if(GroupCounter != fDragGroup)
{
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(GroupCounter);
NumberOfColumnsInGroup = ThisGroup->GroupStopDispListIndex -
ThisGroup->GroupStartDispListIndex + 1;
for(ColumnCounter = ThisGroup->GroupStartDispListIndex; ColumnCounter <=
ThisGroup->GroupStopDispListIndex; ColumnCounter++)
NewDisplayList.AddItem(fDisplayList->ItemAt(ColumnCounter));
}
}
//Set the new order
*fDisplayList = NewDisplayList;
//Update columns and drag groups
fParent->UpdateColumnSizesDataRectSizeScrollBars();
UpdateDragGroups();
SetSnapMinMax();
//Inform the program that the display order changed
int32* NewOrder = fParent->DisplayOrder();
fParent->DisplayOrderChanged(NewOrder);
delete[] NewOrder;
}
void CLVColumnLabelView::UpdateDragGroups()
{
//Make a copy of the DragGroups list. Use it to store the CLVDragGroup's for recycling
BList TempList(fDragGroups);
fDragGroups.MakeEmpty();
int32 NumberOfColumns = fDisplayList->CountItems();
bool ContinueGroup = false;
CLVDragGroup* CurrentGroup;
for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
{
CLVColumn* CurrentColumn = (CLVColumn*)fDisplayList->ItemAt(Counter);
if(!ContinueGroup)
{
//Recycle or obtain a new CLVDragGroup
CurrentGroup = (CLVDragGroup*)TempList.RemoveItem(int32(0));
if(CurrentGroup == NULL)
CurrentGroup = new CLVDragGroup;
//Add the CLVDragGroup to the DragGroups list
fDragGroups.AddItem(CurrentGroup);
//Set up the new DragGroup
CurrentGroup->GroupStartDispListIndex = Counter;
CurrentGroup->GroupStopDispListIndex = Counter;
CurrentGroup->Flags = 0;
if(CurrentColumn->IsShown())
{
CurrentGroup->GroupBegin = CurrentColumn->fColumnBegin;
CurrentGroup->GroupEnd = CurrentColumn->fColumnEnd;
CurrentGroup->LastColumnShown = CurrentColumn;
CurrentGroup->Shown = true;
if(CurrentColumn->fFlags & CLV_LOCK_AT_BEGINNING)
CurrentGroup->AllLockBeginning = true;
else
CurrentGroup->AllLockBeginning = false;
if(CurrentColumn->fFlags & CLV_LOCK_AT_END)
CurrentGroup->AllLockEnd = true;
else
CurrentGroup->AllLockEnd = false;
}
else
{
CurrentGroup->GroupBegin = -1.0;
CurrentGroup->GroupEnd = -1.0;
CurrentGroup->LastColumnShown = NULL;
CurrentGroup->Shown = false;
if(CurrentColumn->fFlags & CLV_LOCK_AT_BEGINNING)
CurrentGroup->AllLockBeginning = true;
else
CurrentGroup->AllLockBeginning = false;
if(CurrentColumn->fFlags & CLV_LOCK_AT_END)
CurrentGroup->AllLockEnd = true;
else
CurrentGroup->AllLockEnd = false;
}
}
else
{
//Add this column to the current DragGroup
CurrentGroup->GroupStopDispListIndex = Counter;
if(CurrentColumn->IsShown())
{
if(CurrentGroup->GroupBegin == -1.0)
CurrentGroup->GroupBegin = CurrentColumn->fColumnBegin;
CurrentGroup->GroupEnd = CurrentColumn->fColumnEnd;
CurrentGroup->LastColumnShown = CurrentColumn;
CurrentGroup->Shown = true;
}
if(!(CurrentColumn->fFlags & CLV_LOCK_AT_BEGINNING))
CurrentGroup->AllLockBeginning = false;
if(!(CurrentColumn->fFlags & CLV_LOCK_AT_END))
CurrentGroup->AllLockEnd = false;
}
CurrentGroup->Flags |= CurrentColumn->fFlags & (CLV_NOT_MOVABLE|CLV_LOCK_AT_BEGINNING|
CLV_LOCK_AT_END);
//See if I should add more columns to this group
if(CurrentColumn->fFlags & CLV_LOCK_WITH_RIGHT)
ContinueGroup = true;
else
ContinueGroup = false;
}
//If any unused groups remain in TempList, delete them
while((CurrentGroup = (CLVDragGroup*)TempList.RemoveItem(int32(0))) != NULL)
delete CurrentGroup;
}
void CLVColumnLabelView::SetSnapMinMax()
{
//Find the column group that the user is dragging and the shown group before it
int32 NumberOfGroups = fDragGroups.CountItems();
int32 ColumnCount;
fDragGroup = -1;
fTheShownGroupBefore = NULL;
fSnapGroupBefore = -1;
CLVDragGroup* ThisGroup;
int32 GroupCounter;
for(GroupCounter = 0; GroupCounter < NumberOfGroups; GroupCounter++)
{
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(GroupCounter);
for(ColumnCount = ThisGroup->GroupStartDispListIndex; ColumnCount <=
ThisGroup->GroupStopDispListIndex; ColumnCount++)
if(fDisplayList->ItemAt(ColumnCount) == fColumnClicked)
{
fDragGroup = GroupCounter;
fTheDragGroup = ThisGroup;
break;
}
if(fDragGroup != -1)
break;
else if(ThisGroup->Shown)
{
fTheShownGroupBefore = ThisGroup;
fSnapGroupBefore = GroupCounter;
}
}
//Find the position of shown group after the one that the user is dragging
fTheShownGroupAfter = NULL;
fSnapGroupAfter = -1;
for(GroupCounter = fDragGroup+1; GroupCounter < NumberOfGroups; GroupCounter++)
{
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(GroupCounter);
if(ThisGroup->Shown)
{
fTheShownGroupAfter = ThisGroup;
fSnapGroupAfter = GroupCounter;
break;
}
}
//See if it can actually snap in the given direction
if(fSnapGroupBefore != -1)
{
if(fTheShownGroupBefore->Flags & CLV_LOCK_AT_BEGINNING)
if(!fTheDragGroup->AllLockBeginning)
fSnapGroupBefore = -1;
if(fTheDragGroup->Flags & CLV_LOCK_AT_END)
if(!fTheShownGroupBefore->AllLockEnd)
fSnapGroupBefore = -1;
}
if(fSnapGroupAfter != -1)
{
if(fTheShownGroupAfter->Flags & CLV_LOCK_AT_END)
if(!fTheDragGroup->AllLockEnd)
fSnapGroupAfter = -1;
if(fTheDragGroup->Flags & CLV_LOCK_AT_BEGINNING)
if(!fTheShownGroupAfter->AllLockBeginning)
fSnapGroupAfter = -1;
}
//Find the minumum and maximum positions for the group to snap
fSnapMin = -1.0;
fSnapMax = -1.0;
fDragBoxWidth = fTheDragGroup->GroupEnd-fTheDragGroup->GroupBegin;
if(fSnapGroupBefore != -1)
{
fSnapMin = fTheShownGroupBefore->GroupBegin + fDragBoxWidth;
if(fSnapMin > fTheShownGroupBefore->GroupEnd)
fSnapMin = fTheShownGroupBefore->GroupEnd;
}
if(fSnapGroupAfter != -1)
{
fSnapMax = fTheShownGroupAfter->GroupEnd - fDragBoxWidth;
if(fSnapMax < fTheShownGroupAfter->GroupBegin)
fSnapMax = fTheShownGroupAfter->GroupBegin;
}
}
@@ -0,0 +1,76 @@
#ifndef CLVColumnLabelView_h
#define CLVColumnLabelView_h
#include <support/SupportDefs.h>
#include <InterfaceKit.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
class ColumnListView;
class CLVColumn;
//******************************************************************************************************
//**** CLASS AND STRUCTURE DECLARATIONS, ASSOCIATED CONSTANTS AND STATIC FUNCTIONS
//******************************************************************************************************
struct CLVDragGroup
{
int32 GroupStartDispListIndex; //Indices in the column display list where this group starts
int32 GroupStopDispListIndex; //and finishes
float GroupBegin,GroupEnd; //-1.0 if whole group is hidden
CLVColumn* LastColumnShown;
bool AllLockBeginning;
bool AllLockEnd;
bool Shown; //False if none of the columns in this group are shown
uint32 Flags; //Uses CLV_NOT_MOVABLE, CLV_LOCK_AT_BEGINNING, CLV_LOCK_AT_END
};
class CLVColumnLabelView : public BView
{
public:
//Constructor and destructor
CLVColumnLabelView(BRect Bounds,ColumnListView* Parent,const BFont* Font);
~CLVColumnLabelView();
//BView overrides
void Draw(BRect UpdateRect);
void MouseDown(BPoint Point);
void MessageReceived(BMessage *message);
private:
friend class ColumnListView;
friend class CLVColumn;
float fFontAscent;
BList* fDisplayList;
//Column select and drag stuff
CLVColumn* fColumnClicked;
BPoint fPreviousMousePos;
BPoint fMouseClickedPos;
bool fColumnDragging;
bool fColumnResizing;
BList fDragGroups; //Groups of CLVColumns that must drag together
int32 fDragGroup; //Index into DragGroups of the group being dragged by user
CLVDragGroup* fTheDragGroup;
CLVDragGroup* fTheShownGroupBefore;
CLVDragGroup* fTheShownGroupAfter;
int32 fSnapGroupBefore, //Index into DragGroups of TheShownGroupBefore and
fSnapGroupAfter; //TheShownGroupAfter, if the group the user is dragging is
//allowed to snap there, otherwise -1
float fDragBoxMouseHoldOffset,fResizeMouseHoldOffset;
float fDragBoxWidth; //Can include multiple columns; depends on CLV_LOCK_WITH_RIGHT
float fPrevDragOutlineLeft,fPrevDragOutlineRight;
float fSnapMin,fSnapMax; //-1.0 indicates the column can't snap in the given direction
ColumnListView* fParent;
//Private functions
void ShiftDragGroup(int32 NewPos);
void UpdateDragGroups();
void SetSnapMinMax();
};
#endif
@@ -0,0 +1,174 @@
//CLVListItem source file
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#define CLVListItem_CPP
#include <stdio.h>
#include "CLVListItem.h"
#include "ColumnListView.h"
#include "CLVColumn.h"
#include "PrefilledBitmap.h"
#include "InterfaceKit.h"
//******************************************************************************************************
//**** CLVItem CLASS DEFINITION
//******************************************************************************************************
CLVListItem::CLVListItem(uint32 level, bool superitem, bool expanded, float minheight)
: BListItem(level, expanded),
fExpanderButtonRect(-1.0,-1.0,-1.0,-1.0),
fExpanderColumnRect(-1.0,-1.0,-1.0,-1.0),
_selectedColumn(-1)
{
fSuperItem = superitem;
fOutlineLevel = level;
fMinHeight = minheight;
}
CLVListItem::~CLVListItem()
{ }
bool CLVListItem::IsSuperItem() const
{
return fSuperItem;
}
void CLVListItem::SetSuperItem(bool superitem)
{
fSuperItem = superitem;
}
uint32 CLVListItem::OutlineLevel() const
{
return fOutlineLevel;
}
void CLVListItem::SetOutlineLevel(uint32 level)
{
fOutlineLevel = level;
}
void CLVListItem::Pulse(BView * owner)
{
// empty
}
void CLVListItem::DrawItem(BView* owner, BRect itemRect, bool complete)
{
BList* DisplayList = &((ColumnListView*)owner)->fColumnDisplayList;
int32 NumberOfColumns = DisplayList->CountItems();
float PushMax = itemRect.right;
CLVColumn* ThisColumn;
BRect ThisColumnRect = itemRect;
float ExpanderDelta = OutlineLevel() * 20.0;
//Figure out what the limit is for expanders pushing other columns
for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
{
ThisColumn = (CLVColumn*)DisplayList->ItemAt(Counter);
if((ThisColumn->fFlags & CLV_EXPANDER) || ThisColumn->fPushedByExpander)
PushMax = ThisColumn->fColumnEnd;
}
BRegion ClippingRegion;
if(!complete)
owner->GetClippingRegion(&ClippingRegion);
else
ClippingRegion.Set(itemRect);
float LastColumnEnd = -1.0;
//Draw the columns
for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
{
ThisColumn = (CLVColumn*)DisplayList->ItemAt(Counter);
if(!ThisColumn->IsShown())
continue;
ThisColumnRect.left = ThisColumn->fColumnBegin;
ThisColumnRect.right = LastColumnEnd = ThisColumn->fColumnEnd;
float Shift = 0.0;
if((ThisColumn->fFlags & CLV_EXPANDER) || ThisColumn->fPushedByExpander)
Shift = ExpanderDelta;
if(ThisColumn->fFlags & CLV_EXPANDER)
{
ThisColumnRect.right += Shift;
if(ThisColumnRect.right > PushMax)
ThisColumnRect.right = PushMax;
fExpanderColumnRect = ThisColumnRect;
if(ClippingRegion.Intersects(ThisColumnRect))
{
//Give the programmer a chance to do his kind of highlighting if the item is selected
int32 actualIndex = ((ColumnListView*)owner)->fColumnList.IndexOf(ThisColumn);
DrawItemColumn(owner, ThisColumnRect, actualIndex, (_selectedColumn == actualIndex), complete);
if(fSuperItem)
{
//Draw the expander, clip manually
float TopOffset = ceil((ThisColumnRect.bottom-ThisColumnRect.top-10.0)/2.0);
float LeftOffset = ThisColumn->fColumnEnd + Shift - 3.0 - 10.0;
float RightClip = LeftOffset + 10.0 - ThisColumnRect.right;
if(RightClip < 0.0)
RightClip = 0.0;
BBitmap* Arrow;
if(IsExpanded())
Arrow = &((ColumnListView*)owner)->fDownArrow;
else
Arrow = &((ColumnListView*)owner)->fRightArrow;
if(LeftOffset <= ThisColumnRect.right)
{
fExpanderButtonRect.Set(LeftOffset,ThisColumnRect.top+TopOffset,
LeftOffset+10.0-RightClip,ThisColumnRect.top+TopOffset+10.0);
owner->SetDrawingMode(B_OP_OVER);
owner->DrawBitmap(Arrow, BRect(0.0,0.0,10.0-RightClip,10.0),fExpanderButtonRect);
owner->SetDrawingMode(B_OP_COPY);
}
else
fExpanderButtonRect.Set(-1.0,-1.0,-1.0,-1.0);
}
}
}
else
{
ThisColumnRect.left += Shift;
ThisColumnRect.right += Shift;
if(Shift > 0.0 && ThisColumnRect.right > PushMax)
ThisColumnRect.right = PushMax;
if(ThisColumnRect.right >= ThisColumnRect.left && ClippingRegion.Intersects(ThisColumnRect))
{
int32 actualIndex = ((ColumnListView*)owner)->fColumnList.IndexOf(ThisColumn);
DrawItemColumn(owner, ThisColumnRect, actualIndex, (_selectedColumn == actualIndex), complete);
}
}
}
//Fill the area after all the columns (so the select highlight goes all the way across)
ThisColumnRect.left = LastColumnEnd + 1.0;
ThisColumnRect.right = owner->Bounds().right;
if(ThisColumnRect.left <= ThisColumnRect.right && ClippingRegion.Intersects(ThisColumnRect))
{
DrawItemColumn(owner, ThisColumnRect,-1, false, complete);
}
}
float CLVListItem::ExpanderShift(int32 column_index, BView* owner)
{
BList* DisplayList = &((ColumnListView*)owner)->fColumnDisplayList;
CLVColumn* ThisColumn = (CLVColumn*)DisplayList->ItemAt(column_index);
float ExpanderDelta = OutlineLevel() * 20.0;
if(!ThisColumn->fPushedByExpander)
ExpanderDelta = 0.0;
return ExpanderDelta;
}
void CLVListItem::Update(BView* owner, const BFont* font)
{
BListItem::Update(owner,font);
float ItemHeight = Height();
if(ItemHeight < fMinHeight)
ItemHeight = fMinHeight;
SetWidth(((ColumnListView*)owner)->fPageWidth);
SetHeight(ItemHeight);
}
@@ -0,0 +1,63 @@
#ifndef CLVListItem_h
#define CLVListItem_h
#include <interface/ListItem.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
class ColumnListView;
//******************************************************************************************************
//**** CLVItem CLASS DECLARATION
//******************************************************************************************************
class CLVListItem : public BListItem
{
public:
//Constructor and destructor
CLVListItem(uint32 level = 0, bool superitem = false, bool expanded = false, float minheight = 0.0);
virtual ~CLVListItem();
//Archival stuff
/* Not implemented yet
CLVItem(BMessage* archive);
static CLVItem* Instantiate(BMessage* data);
virtual status_t Archive(BMessage* data, bool deep = true) const;
*/
virtual void DrawItemColumn(BView* owner, BRect item_column_rect, int32 column_index, bool columnSelected,
bool complete) = 0; //column_index (0-N) is based on the order in which the columns were added
//to the ColumnListView, not the display order. An index of -1 indicates
//that the program needs to draw a blank area beyond the last column. The
//main purpose is to allow the highlighting bar to continue all the way to
//the end of the ColumnListView, even after the end of the last column.
virtual void DrawItem(BView* owner, BRect itemRect, bool complete);
//In general, you don't need or want to override DrawItem().
float ExpanderShift(int32 column_index, BView* owner);
virtual void Update(BView* owner, const BFont* font);
bool IsSuperItem() const;
void SetSuperItem(bool superitem);
uint32 OutlineLevel() const;
void SetOutlineLevel(uint32 level);
virtual void Pulse(BView * owner); // Called periodically when this item is selected.
int32 GetSelectedColumn() const {return _selectedColumn;}
void SetSelectedColumn(int32 i) {_selectedColumn = i;}
private:
friend class ColumnListView;
bool fSuperItem;
uint32 fOutlineLevel;
float fMinHeight;
BRect fExpanderButtonRect;
BRect fExpanderColumnRect;
BList* fSortingContextBList;
ColumnListView* fSortingContextCLV;
int32 _selectedColumn;
};
#endif
+45
View File
@@ -0,0 +1,45 @@
#ifndef JColors_h
#define JColors_h
//Useful until be gets around to making these sorts of things
//globals akin to be_plain_font, etc.
#include <interface/ColorControl.h>
//Be standard UI colors
const rgb_color BeBackgroundGrey = {216,216,216, 255};
const rgb_color BeInactiveControlGrey = {240,240,240, 255};
const rgb_color BeFocusBlue = {0, 0, 229, 255};
const rgb_color BeHighlight = {255,255,255, 255};
const rgb_color BeShadow = {152,152,152, 255};
const rgb_color BeDarkShadow = {108,108,108, 255};
const rgb_color BeLightShadow = {194,194,194, 255};
const rgb_color BeButtonGrey = {232,232,232, 255};
const rgb_color BeInactiveGrey = {127,127,127, 255};
const rgb_color BeListSelectGrey = {178,178,178, 255};
const rgb_color BeTitleBarYellow = {255,203,0, 255};
//Other colors
const rgb_color Black = {0, 0, 0, 255};
const rgb_color White = {255,255,255, 255};
const rgb_color Red = {255,0, 0, 255};
const rgb_color Green = {0, 167,0, 255};
const rgb_color LightGreen = {90, 240,90, 255};
const rgb_color Blue = {49, 61, 225, 255};
const rgb_color LightBlue = {64, 162,255, 255};
const rgb_color Purple = {144,64, 221, 255};
const rgb_color LightPurple = {166,74, 255, 255};
const rgb_color Lavender = {193,122,255, 255};
const rgb_color Yellow = {255,203,0, 255};
const rgb_color Orange = {255,163,0, 255};
const rgb_color Flesh = {255,231,186, 255};
const rgb_color Tan = {208,182,121, 255};
const rgb_color Brown = {154,110,45, 255};
const rgb_color LightMetallicBlue = {143,166,240, 255};
const rgb_color MedMetallicBlue = {75, 96, 154, 255};
const rgb_color DarkMetallicBlue = {78, 89, 126, 255};
const rgb_color ReallyLightPurple = {255,210,255, 255};
const rgb_color LightYellow = {255,255,210, 255};
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,201 @@
#ifndef ColumnListView_h
#define ColumnListView_h
//Column list view header file
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
#include <interface/ListView.h>
#include "Colors.h"
#include "CLVColumn.h"
class CLVListItem;
class CLVColumnLabelView;
class CLVFillerView;
class CLVContainerView;
#include "PrefilledBitmap.h"
#include "ScrollViewCorner.h"
//******************************************************************************************************
//**** CONSTANTS AND TYPE DEFINITIONS
//******************************************************************************************************
typedef int (*CLVCompareFuncPtr)(const CLVListItem* item1, const CLVListItem* item2, int32 sort_key);
//******************************************************************************************************
//**** ColumnListView CLASS DECLARATION
//******************************************************************************************************
class ColumnListView : public BListView
{
public:
//Constructor and destructor
ColumnListView( BRect Frame,
BScrollView** ContainerView, //Used to get back a pointer to the container
//view that will hold the ColumnListView, the
//the CLVColumnLabelView, and the scrollbars.
//If no scroll bars or border are asked for,
//this will act like a plain BView container.
const char* Name = NULL,
uint32 ResizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP,
uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE,
list_view_type Type = B_SINGLE_SELECTION_LIST,
bool hierarchical = false,
bool horizontal = true, //Which scroll bars should I add, if any
bool vertical = true,
border_style border = B_NO_BORDER, //What type of border to add, if any
const BFont* LabelFont = be_plain_font);
virtual ~ColumnListView();
void AddScrollViewCorner();
//Archival stuff
/*** Not implemented yet
ColumnListView(BMessage* archive);
static ColumnListView* Instantiate(BMessage* data);
virtual status_t Archive(BMessage* data, bool deep = true) const;
***/
virtual void MessageReceived(BMessage * msg);
//Column setup functions
virtual bool AddColumn(CLVColumn* Column); //Note that a column may only be added to
//one ColumnListView at a time, and may not
//be added more than once to the same
//ColumnListView without removing it
//inbetween
virtual bool AddColumnList(BList* NewColumns);
virtual bool RemoveColumn(CLVColumn* Column);
virtual bool RemoveColumns(CLVColumn* Column, int32 Count); //Finds Column in ColumnList
//and removes Count columns and
//their data from the view
//and its items
int32 CountColumns() const;
int32 IndexOfColumn(CLVColumn* column) const;
CLVColumn * ColumnAt(BPoint point) const; // Returns column located at point on screen --added by jaf
CLVColumn* ColumnAt(int32 column_index) const;
virtual bool SetDisplayOrder(const int32* Order);
//Sets the display order: each int32 in the Order list specifies the column index of the
//next column to display. Note that this DOES NOT get called if the user drags a
//column, so overriding it will not inform you of user changes. If you need that info,
//override DisplayOrderChanged instead. Also note that SetDisplayOrder does call
//DisplayOrderChanged(false).
virtual void ColumnWidthChanged(int32 ColumnIndex, float NewWidth);
virtual void DisplayOrderChanged(const int32* order);
//Override this if you want to find out when the display order changed.
int32* DisplayOrder() const; //Gets the display order in the same format as that used by
//SetDisplayOrder. The returned array belongs to the caller and
//must be delete[]'d when done with it.
virtual void SetSortKey(int32 ColumnIndex);
//Set it to -1 to remove the sort key.
virtual void AddSortKey(int32 ColumnIndex);
void ReverseSortMode(int32 ColumnIndex);
virtual void SetSortMode(int32 ColumnIndex,CLVSortMode Mode);
int32 Sorting(int32* SortKeys, CLVSortMode* SortModes) const;
//Returns the number of used sort keys, and fills the provided arrays with the sort keys
//by column index and sort modes, in priority order. The pointers should point to an array
//int32 SortKeys[n], and an array CLVSortMode SortModes[n] where n is the number of sortable
//columns in the ColumnListView. Note: sorting will only occur if the key column is shown.
void SetSorting(int32 NumberOfKeys, int32* SortKeys, CLVSortMode* SortModes);
//Sets the sorting parameters using the same format returned by Sorting
//BView overrides
virtual void FrameResized(float Width, float Height);
virtual void AttachedToWindow();
virtual void ScrollTo(BPoint point);
virtual void MouseDown(BPoint point);
//List functions
virtual bool AddUnder(CLVListItem*, CLVListItem* superitem);
virtual bool AddItem(CLVListItem*, int32 fullListIndex);
virtual bool AddItem(CLVListItem*);
virtual bool AddList(BList* newItems); //This must be a BList of
//CLVListItem*'s, NOT BListItem*'s
virtual bool AddList(BList* newItems, int32 fullListIndex); //This must be a BList of
//CLVListItem*'s, NOT BListItem*'s
virtual bool RemoveItem(CLVListItem* item);
virtual BListItem* RemoveItem(int32 fullListIndex); //Actually returns CLVListItem
virtual bool RemoveItems(int32 fullListIndex, int32 count);
virtual void MakeEmpty();
CLVListItem* FullListItemAt(int32 fullListIndex) const;
int32 FullListIndexOf(const CLVListItem* item) const;
int32 FullListIndexOf(BPoint point) const;
CLVListItem* FullListFirstItem() const;
CLVListItem* FullListLastItem() const;
bool FullListHasItem(const CLVListItem* item) const;
int32 FullListCountItems() const;
bool FullListIsEmpty() const;
int32 FullListCurrentSelection(int32 index = 0) const;
void FullListDoForEach(bool (*func)(CLVListItem*));
void FullListDoForEach(bool (*func)(CLVListItem*, void*), void* arg2);
CLVListItem* Superitem(const CLVListItem* item) const;
int32 FullListNumberOfSubitems(const CLVListItem* item) const;
virtual void Expand(CLVListItem* item);
virtual void Collapse(CLVListItem* item);
bool IsExpanded(int32 fullListIndex) const;
void SetSortFunction(CLVCompareFuncPtr compare);
void SortItems();
virtual void KeyDown(const char * bytes, int32 numBytes);
void SetEditMessage(BMessage * newMsg, BMessenger target);
// Sets a BMessage that will be sent every time a key is pressed, or the mouse
// is clicked in the active cell. (newMsg) becomes property of this ColumnListView.
// Copies of (newMsg) will be sent to (target).
virtual void Pulse();
// Used to make the cursor blink on the string column...
int32 GetSelectedColumn() const {return _selectedColumn;}
private:
friend class CLVMainView;
friend class CLVColumn;
friend class CLVColumnLabelView;
friend class CLVListItem;
int32 GetActualIndexOf(int32 displayIndex) const;
// Returns the "real" index of the given display index, or -1 if there is none.
int32 GetDisplayIndexOf(int32 actualIndex) const;
// Returns the display index of the given "real" index, or -1 if there is none.
void SetSelectedColumnIndex(int32 selectedColumnIndex);
// Call this to change _selectedColumn to a new value properly.
void UpdateColumnSizesDataRectSizeScrollBars();
void UpdateScrollBars();
void ColumnsChanged();
void CreateContainer(bool horizontal, bool vertical, border_style border, uint32 ResizingMode,
uint32 flags);
void SortListArray(CLVListItem** SortArray, int32 NumberOfItems);
void MakeEmptyPrivate();
bool AddListPrivate(BList* newItems, int32 fullListIndex);
bool AddItemPrivate(CLVListItem* item, int32 fullListIndex);
void SortFullListSegment(int32 OriginalListStartIndex, int32 InsertionPoint, BList* NewList);
BList* SortItemsInThisLevel(int32 OriginalListStartIndex);
static int PlainBListSortFunc(BListItem** item1, BListItem** item2);
static int HierarchicalBListSortFunc(BListItem** item1, BListItem** item2);
CLVColumnLabelView* fColumnLabelView;
CLVContainerView* fScrollView;
ScrollViewCorner* fFillerView;
bool fHierarchical;
BList fColumnList;
BList fColumnDisplayList;
float fDataWidth,fDataHeight,fPageWidth,fPageHeight;
BList fSortKeyList; //List contains CLVColumn pointers
PrefilledBitmap fRightArrow;
PrefilledBitmap fDownArrow;
BList fFullItemList;
int32 fExpanderColumn;
CLVCompareFuncPtr fCompare;
// added by jaf
int32 _selectedColumn; // actual index of the column that contains the active cell.
BMessage * _editMessage; // if non-NULL, sent on keypress or when active cell is clicked.
BMessenger _editTarget; // target for _editMessage.
};
#endif
@@ -0,0 +1,72 @@
#include "MouseWatcher.h"
#include <Messenger.h>
#include <InterfaceKit.h>
int32 MouseWatcher(void* data);
thread_id StartMouseWatcher(BView* TargetView)
{
thread_id MouseWatcherThread = spawn_thread(MouseWatcher,"MouseWatcher",B_NORMAL_PRIORITY,
new BMessenger(TargetView));
if(MouseWatcherThread != B_NO_MORE_THREADS && MouseWatcherThread != B_NO_MEMORY)
resume_thread(MouseWatcherThread);
return MouseWatcherThread;
}
int32 MouseWatcher(void* data)
{
BMessenger* TheMessenger = (BMessenger*)data;
BPoint PreviousPos;
uint32 PreviousButtons;
bool FirstCheck = true;
BMessage MessageToSend;
MessageToSend.AddPoint("where",BPoint(0,0));
MessageToSend.AddInt32("buttons",0);
MessageToSend.AddInt32("modifiers",0);
while(true)
{
if (!TheMessenger->LockTarget())
{
delete TheMessenger;
return 0; // window is dead so exit
}
BLooper *TheLooper;
BView* TheView = (BView*)TheMessenger->Target(&TheLooper);
BPoint Where;
uint32 Buttons;
TheView->GetMouse(&Where,&Buttons,false);
if(FirstCheck)
{
PreviousPos = Where;
PreviousButtons = Buttons;
FirstCheck = false;
}
bool Send = false;
if(Buttons != PreviousButtons || Buttons == 0 || Where != PreviousPos)
{
if(Buttons == 0)
MessageToSend.what = MW_MOUSE_UP;
else if(Buttons != PreviousButtons)
MessageToSend.what = MW_MOUSE_DOWN;
else
MessageToSend.what = MW_MOUSE_MOVED;
MessageToSend.ReplacePoint("where",Where);
MessageToSend.ReplaceInt32("buttons",Buttons);
MessageToSend.ReplaceInt32("modifiers",modifiers());
Send = true;
}
TheLooper->Unlock();
if(Send)
TheMessenger->SendMessage(&MessageToSend);
if(Buttons == 0)
{
//Button was released
delete TheMessenger;
return 0;
}
snooze(50000);
}
}
@@ -0,0 +1,39 @@
/****DOCUMENTATION
Once started, MouseWatcher will watch the mouse until the mouse buttons are all released, sending
messages to the target BView (TargetView is specified as the target handler in the BMessenger used to
send the messages. The BLooper == window of the target view is determined automatically by the
BMessenger)
If the mouse moves, a MW_MOUSE_MOVED message is sent.
If the mouse buttons are changed, but not released, a MW_MOUSE_DOWN message is sent.
If the mouse button(s) are released, a MW_MOUSE_UP message is sent.
These messages will have three data entries:
"where" (B_POINT_TYPE) - The position of the mouse in TargetView's coordinate system.
"buttons" (B_INT32_TYPE) - The mouse buttons. See BView::GetMouse().
"modifiers" (B_INT32_TYPE) - The modifier keys held down at the time. See modifiers().
Once it is started, you can't stop it, but that shouldn't matter - the user will most likely release
the buttons soon, and you can interpret the events however you want.
StartMouseWatcher returns a valid thread ID, or it returns an error code:
B_NO_MORE_THREADS. all thread_id numbers are currently in use.
B_NO_MEMORY. Not enough memory to allocate the resources for another thread.
****/
#ifndef MouseWatcher_h
#define MouseWatcher_h
#include <SupportDefs.h>
#include <OS.h>
class BView;
const uint32 MW_MOUSE_DOWN = 'Mw-D';
const uint32 MW_MOUSE_UP = 'Mw-U';
const uint32 MW_MOUSE_MOVED = 'Mw-M';
thread_id StartMouseWatcher(BView* TargetView);
#endif
@@ -0,0 +1,14 @@
#include "PrefilledBitmap.h"
PrefilledBitmap::PrefilledBitmap(BRect bounds, color_space space, const void *data, bool acceptsViews,
bool needsContiguousMemory)
: BBitmap(bounds, space, acceptsViews, needsContiguousMemory)
{
int32 length = ((int32(bounds.right-bounds.left)+3) / 4) * 4;
length *= int32(bounds.bottom-bounds.top)+1;
SetBits(data, length, 0, space);
}
PrefilledBitmap::~PrefilledBitmap()
{ }
@@ -0,0 +1,18 @@
#ifndef PreFilledBitmap_h
#define PreFilledBitmap_h
#include <interface/Bitmap.h>
//Useful until be implements BBitmap::BBitmap(BRect bounds, color_space space, const void *data,
// bool acceptsViews, bool needsContiguousMemory)
//or something like it...
class PrefilledBitmap : public BBitmap
{
public:
PrefilledBitmap(BRect bounds, color_space space, const void *data, bool acceptsViews,
bool needsContiguousMemory);
~PrefilledBitmap();
};
#endif
@@ -0,0 +1,28 @@
#include "Colors.h"
#include "ScrollViewCorner.h"
#include <InterfaceKit.h>
ScrollViewCorner::ScrollViewCorner(float Left,float Top)
: BView(BRect(Left,Top,Left+B_V_SCROLL_BAR_WIDTH,Top+B_H_SCROLL_BAR_HEIGHT),NULL,B_FOLLOW_RIGHT |
B_FOLLOW_BOTTOM,B_WILL_DRAW)
{
SetHighColor(BeShadow);
SetViewColor(BeInactiveGrey);
}
ScrollViewCorner::~ScrollViewCorner()
{ }
void ScrollViewCorner::Draw(BRect Update)
{
if(Update.bottom >= B_H_SCROLL_BAR_HEIGHT)
StrokeLine(BPoint(0.0,B_H_SCROLL_BAR_HEIGHT),BPoint(B_V_SCROLL_BAR_WIDTH,B_H_SCROLL_BAR_HEIGHT));
if(Update.right >= B_V_SCROLL_BAR_WIDTH)
StrokeLine(BPoint(B_V_SCROLL_BAR_WIDTH,0.0),
BPoint(B_V_SCROLL_BAR_WIDTH,B_H_SCROLL_BAR_HEIGHT-1.0));
}
@@ -0,0 +1,21 @@
#ifndef ScrollViewCorner_h
#define ScrollViewCorner_h
//If you have a BScrollView with horizontal and vertical sliders that isn't
//seated to the lower-right corner of a B_DOCUMENT_WINDOW, there's a "hole"
//between the sliders that needs to be filled. You can use this to fill it.
//In general, it looks best to set the ScrollViewCorner color to
//BeInactiveControlGrey if the vertical BScrollBar is inactive, and the color
//to BeBackgroundGrey if the vertical BScrollBar is active. Have a look at
//Demo3 of ColumnListView to see what I mean if this is unclear.
class ScrollViewCorner : public BView
{
public:
ScrollViewCorner(float Left,float Top);
~ScrollViewCorner();
void Draw(BRect Update);
};
#endif
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 1999-2009 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jeremy Friesner
*/
#include "ShortcutsApp.h"
#include "KeyInfos.h"
int main(int argc, char** argv)
{
InitKeyIndices();
(new ShortcutsApp)->Run();
delete be_app;
}