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:
@@ -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 ;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user