- added recent documents sub menu

- added resize window to image size when file is opened
- added drag and drop selection
- added copy selection to clipboard


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5288 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Pfeiffer 2003-11-09 07:57:55 +00:00
parent ccd79dfdce
commit d10d59475c
7 changed files with 396 additions and 38 deletions

View File

@ -127,6 +127,10 @@ ShowImageApp::MessageReceived(BMessage *pmsg)
// start checking count of open windows
StartPulse();
break;
case MSG_UPDATE_RECENT_DOCUMENTS:
BroadcastToWindows(MSG_UPDATE_RECENT_DOCUMENTS);
break;
default:
BApplication::MessageReceived(pmsg);
@ -155,3 +159,14 @@ ShowImageApp::Open(const entry_ref *pref)
{
new ShowImageWindow(pref);
}
void
ShowImageApp::BroadcastToWindows(uint32 what)
{
const int32 n = CountWindows();
for (int32 i = 0; i < n ; i ++) {
// BMessenger checks for us if BWindow is still a valid object
BMessenger msgr(WindowAt(i));
msgr.SendMessage(what);
}
}

View File

@ -46,6 +46,7 @@ public:
private:
void StartPulse();
void Open(const entry_ref *pref);
void BroadcastToWindows(uint32 what);
BFilePanel *fpOpenPanel;
bool fbPulseStarted;

View File

@ -57,6 +57,7 @@ const uint32 MSG_INVERT = 'mINV';
const uint32 MSG_DIA_SHOW = 'mDIA';
const uint32 MSG_DIA_SHOW_DELAY = 'mDSD';
const uint32 MSG_FULL_SCREEN = 'mFSC';
const uint32 MSG_UPDATE_RECENT_DOCUMENTS = 'mURD';
extern const char *APP_SIG;

View File

@ -41,6 +41,10 @@
#include <Rect.h>
#include <SupportDefs.h>
#include <Directory.h>
#include <Application.h>
#include <Roster.h>
#include <NodeInfo.h>
#include <Clipboard.h>
#include "ShowImageConstants.h"
#include "ShowImageView.h"
@ -114,10 +118,17 @@ ShowImageView::RotatePatterns()
memset(fPatternRight.data, p, 8);
}
void
ShowImageView::AnimateSelection(bool a)
{
fAnimateSelection = a;
}
void
ShowImageView::Pulse()
{
if (fbHasSelection) {
// animate marching ants
if (fbHasSelection && fAnimateSelection && Window()->IsActive()) {
RotatePatterns();
DrawSelectionBox(fSelectionRect);
}
@ -141,10 +152,12 @@ ShowImageView::ShowImageView(BRect rect, const char *name, uint32 resizingMode,
fpBitmap = NULL;
fDocumentIndex = 1;
fDocumentCount = 1;
fAnimateSelection = true;
fbHasSelection = false;
fResizeToViewBounds = false;
fDiaShow = false;
SetDiaShowDelay(3); // 3 seconds
fBeginDrag = false;
SetViewColor(B_TRANSPARENT_COLOR);
SetHighColor(kborderColor);
@ -181,7 +194,8 @@ ShowImageView::IsImage(const entry_ref *pref)
}
// send message to parent about new image
void ShowImageView::Notify(const char* status)
void
ShowImageView::Notify(const char* status)
{
BMessage msg(MSG_UPDATE_STATUS);
if (status != NULL) {
@ -195,6 +209,13 @@ void ShowImageView::Notify(const char* status)
Invalidate();
}
void
ShowImageView::AddToRecentDocuments()
{
be_roster->AddToRecentDocuments(&fCurrentRef, APP_SIG);
be_app_messenger.SendMessage(MSG_UPDATE_RECENT_DOCUMENTS);
}
void
ShowImageView::SetImage(const entry_ref *pref)
{
@ -241,6 +262,8 @@ ShowImageView::SetImage(const entry_ref *pref)
fDocumentCount = documentCount;
else
fDocumentCount = 1;
AddToRecentDocuments();
Notify(info.name);
}
@ -261,6 +284,14 @@ ShowImageView::GetBitmap()
return fpBitmap;
}
void
ShowImageView::FlushToLeftTop()
{
BRect rect = AlignBitmap();
BPoint p(rect.left, rect.top);
ScrollTo(p);
}
void
ShowImageView::AttachedToWindow()
{
@ -394,9 +425,200 @@ ShowImageView::FrameResized(float /* width */, float /* height */)
void
ShowImageView::ConstrainToImage(BPoint &point)
{
point.ConstrainTo(
fpBitmap->Bounds()
);
point.ConstrainTo(fpBitmap->Bounds());
}
BBitmap*
ShowImageView::CopySelection(uchar alpha)
{
bool hasAlpha = alpha != 255;
if (!fbHasSelection) return NULL;
BRect rect(0, 0, fSelectionRect.IntegerWidth(), fSelectionRect.IntegerHeight());
BView view(rect, NULL, B_FOLLOW_NONE, B_WILL_DRAW);
BBitmap *bitmap = new BBitmap(rect, hasAlpha ? B_RGBA32 : fpBitmap->ColorSpace(), true);
if (bitmap == NULL) return NULL;
if (bitmap->Lock()) {
bitmap->AddChild(&view);
view.DrawBitmap(fpBitmap, fSelectionRect, rect);
if (hasAlpha) {
view.SetDrawingMode(B_OP_SUBTRACT);
view.SetHighColor(0, 0, 0, 255-alpha);
view.FillRect(rect, B_SOLID_HIGH);
}
view.Sync();
bitmap->RemoveChild(&view);
bitmap->Unlock();
}
return bitmap;
}
bool
ShowImageView::AddSupportedTypes(BMessage* msg, BBitmap* bitmap)
{
bool found = false;
BTranslatorRoster *roster = BTranslatorRoster::Default();
if (roster == NULL) return false;
BBitmapStream stream(bitmap);
translator_info *outInfo;
int32 outNumInfo;
if (roster->GetTranslators(&stream, NULL, &outInfo, &outNumInfo) == B_OK) {
for (int32 i = 0; i < outNumInfo; i++) {
const translation_format *fmts;
int32 num_fmts;
roster->GetOutputFormats(outInfo[i].translator, &fmts, &num_fmts);
for (int32 j = 0; j < num_fmts; j++) {
if (strcmp(fmts[j].MIME, "image/x-be-bitmap") != 0) {
// needed to send data in message
msg->AddString("be:types", fmts[j].MIME);
// needed to pass data via file
msg->AddString("be:filetypes", fmts[j].MIME);
msg->AddString("be:type_descriptions", fmts[j].name);
}
found = true;
}
}
}
stream.DetachBitmap(&bitmap);
return found;
}
void
ShowImageView::BeginDrag(BPoint point)
{
// mouse moved?
if (!fBeginDrag) return;
point = ViewToImage(point);
if (point == fFirstPoint) return;
fBeginDrag = false;
BBitmap* bitmap = CopySelection(128);
if (bitmap == NULL) return;
SetMouseEventMask(B_POINTER_EVENTS);
BPoint leftTop(fSelectionRect.left, fSelectionRect.top);
// fill the drag message
BMessage drag(B_SIMPLE_DATA);
drag.AddInt32("be:actions", B_COPY_TARGET);
drag.AddString("be:clip_name", "Bitmap Clip");
// XXX undocumented fields
drag.AddPoint("be:_source_point", fFirstPoint);
drag.AddRect("be:_frame", fSelectionRect);
// XXX meaning unknown???
// drag.AddInt32("be:_format", e.g.: B_TGA_FORMAT);
// drag.AddInt32("be_translator", translator_id);
// drag.AddPointer("be:_bitmap_ptr, ?);
if (AddSupportedTypes(&drag, bitmap)) {
// we also support "Passing Data via File" protocol
drag.AddString("be:types", B_FILE_MIME_TYPE);
// avoid flickering of dragged bitmap caused by drawing into the window
AnimateSelection(false);
// DragMessage takes ownership of bitmap
DragMessage(&drag, bitmap, B_OP_ALPHA, fFirstPoint - leftTop);
}
}
bool
ShowImageView::OutputFormatForType(BBitmap* bitmap, const char* type, translation_format* format)
{
bool found = false;
BTranslatorRoster *roster = BTranslatorRoster::Default();
if (roster == NULL) return false;
BBitmapStream stream(bitmap);
translator_info *outInfo;
int32 outNumInfo;
if (roster->GetTranslators(&stream, NULL, &outInfo, &outNumInfo) == B_OK) {
for (int32 i = 0; i < outNumInfo; i++) {
const translation_format *fmts;
int32 num_fmts;
roster->GetOutputFormats(outInfo[i].translator, &fmts, &num_fmts);
for (int32 j = 0; j < num_fmts; j++) {
if (strcmp(fmts[j].MIME, type) == 0) {
*format = fmts[j];
found = true;
break;
}
}
}
}
stream.DetachBitmap(&bitmap);
return found;
}
void
ShowImageView::SaveToFile(BDirectory* dir, const char* name, BBitmap* bitmap, translation_format* format)
{
BTranslatorRoster *roster = BTranslatorRoster::Default();
BBitmapStream stream(bitmap); // destructor deletes bitmap
// write data
BFile file(dir, name, B_WRITE_ONLY);
roster->Translate(&stream, NULL, NULL, &file, format->type);
// set mime type
BNodeInfo info(&file);
if (info.InitCheck() == B_OK) {
info.SetType(format->MIME);
}
}
void
ShowImageView::SendInMessage(BMessage* msg, BBitmap* bitmap, translation_format* format)
{
BMessage reply(B_MIME_DATA);
BBitmapStream stream(bitmap); // destructor deletes bitmap
BTranslatorRoster *roster = BTranslatorRoster::Default();
BMallocIO memStream;
if (roster->Translate(&stream, NULL, NULL, &memStream, format->type) == B_OK) {
reply.AddData(format->MIME, B_MIME_TYPE, memStream.Buffer(), memStream.BufferLength());
msg->SendReply(&reply);
}
}
void
ShowImageView::HandleDrop(BMessage* msg)
{
BMessage data(B_MIME_DATA);
entry_ref dirRef;
BString name, type;
bool saveToFile;
bool sendInMessage;
BBitmap *bitmap;
saveToFile = msg->FindString("be:filetypes", &type) == B_OK &&
msg->FindRef("directory", &dirRef) == B_OK &&
msg->FindString("name", &name) == B_OK;
sendInMessage = (!saveToFile) && msg->FindString("be:types", &type) == B_OK;
fprintf(stderr, "HandleDrop saveToFile %s, sendInMessage %s\n",
saveToFile ? "yes" : "no",
sendInMessage ? "yes" : "no");
bitmap = CopySelection();
if (bitmap == NULL) return;
translation_format format;
if (!OutputFormatForType(bitmap, type.String(), &format)) {
delete bitmap;
return;
}
if (saveToFile) {
BDirectory dir(&dirRef);
SaveToFile(&dir, name.String(), bitmap, &format);
} else if (sendInMessage) {
SendInMessage(msg, bitmap, &format);
} else {
delete bitmap;
}
}
void
@ -405,7 +627,8 @@ ShowImageView::MouseDown(BPoint point)
point = ViewToImage(point);
if (fbHasSelection && fSelectionRect.Contains(point)) {
// TODO: start drag and drop
// delay drag until mouse is moved
fBeginDrag = true; fFirstPoint = point;
} else {
// begin new selection
fMakesSelection = true;
@ -447,6 +670,8 @@ ShowImageView::MouseMoved(BPoint point, uint32 state, const BMessage *pmsg)
{
if (fMakesSelection) {
UpdateSelectionRect(point, false);
} else {
BeginDrag(point);
}
}
@ -456,13 +681,20 @@ ShowImageView::MouseUp(BPoint point)
if (fMakesSelection) {
UpdateSelectionRect(point, true);
fMakesSelection = false;
} else {
BeginDrag(point);
}
//fBeginDrag = false;
AnimateSelection(true);
}
void
ShowImageView::MessageReceived(BMessage *pmsg)
{
switch (pmsg->what) {
case B_COPY_TARGET:
HandleDrop(pmsg);
break;
default:
BView::MessageReceived(pmsg);
break;
@ -521,6 +753,53 @@ ShowImageView::PageCount()
return fDocumentCount;
}
void
ShowImageView::SelectAll()
{
fbHasSelection = true;
fSelectionRect.Set(0, 0, fpBitmap->Bounds().Width(), fpBitmap->Bounds().Height());
Invalidate();
}
void
ShowImageView::Unselect()
{
if (fbHasSelection) {
fbHasSelection = false;
Invalidate();
}
}
void
ShowImageView::CopySelectionToClipboard()
{
if (fbHasSelection && be_clipboard->Lock()) {
be_clipboard->Clear();
BMessage *clip = NULL;
if ((clip = be_clipboard->Data()) != NULL) {
BMessage data;
BBitmap* bitmap = CopySelection();
if (bitmap != NULL) {
#if 1
// According to BeBook and Becasso, Gobe Productive do the following.
// Paste works in Productive, but not in Becasso and original ShowImage.
BMessage msg(B_OK); // Becasso uses B_TRANSLATOR_BITMAP, BeBook says its unused
bitmap->Archive(&msg);
clip->AddMessage("image/x-be-bitmap", &msg);
#else
// original ShowImage performs this. Paste works with original ShowImage.
bitmap->Archive(clip);
// original ShowImage uses be:location for insertion point
clip->AddPoint("be:location", BPoint(fSelectionRect.left, fSelectionRect.top));
#endif
delete bitmap;
be_clipboard->Commit();
}
}
be_clipboard->Unlock();
}
}
void
ShowImageView::FirstPage()
{

View File

@ -33,6 +33,7 @@
#include <Bitmap.h>
#include <Entry.h>
#include <String.h>
#include <TranslatorRoster.h>
class ShowImageView : public BView {
public:
@ -45,6 +46,7 @@ public:
void SetImage(const entry_ref *pref);
void ResizeToViewBounds(bool resize);
BBitmap *GetBitmap();
void FlushToLeftTop();
virtual void AttachedToWindow();
virtual void Draw(BRect updateRect);
@ -59,6 +61,11 @@ public:
int32 CurrentPage();
int32 PageCount();
void SelectAll();
void Unselect();
void CopySelectionToClipboard();
void FirstPage();
void LastPage();
void NextPage();
@ -84,7 +91,9 @@ private:
};
void InitPatterns();
void RotatePatterns();
void AnimateSelection(bool a);
void Notify(const char* status);
void AddToRecentDocuments();
int32 BytesPerPixel(color_space cs) const;
inline void CopyPixel(uchar* dest, int32 destX, int32 destY, int32 destBPR, uchar* src, int32 x, int32 y, int32 bpr, int32 bpp);
inline void InvertPixel(int32 x, int32 y, uchar* dest, int32 destBPR, uchar* src, int32 bpr, int32 bpp);
@ -100,6 +109,13 @@ private:
bool ShowNextImage(bool next, bool rewind);
bool FirstFile();
void ConstrainToImage(BPoint &point);
BBitmap* CopySelection(uchar alpha = 255);
bool AddSupportedTypes(BMessage* msg, BBitmap* bitmap);
void BeginDrag(BPoint point);
void SaveToFile(BDirectory* dir, const char* name, BBitmap* bitmap, translation_format* format);
void SendInMessage(BMessage* msg, BBitmap* bitmap, translation_format* format);
bool OutputFormatForType(BBitmap* bitmap, const char* type, translation_format* format);
void HandleDrop(BMessage* msg);
void UpdateSelectionRect(BPoint point, bool final);
void DrawBorder(BRect border);
void DrawSelectionBox(BRect &rect);
@ -113,8 +129,10 @@ private:
float fTop;
float fScaleX;
float fScaleY;
bool fBeginDrag;
bool fMakesSelection; // is a selection being made
BPoint fFirstPoint; // first point in image space of selection
bool fAnimateSelection; // marching ants
bool fbHasSelection; // is fSelectionRect valid
BRect fSelectionRect; // the selection in image space
pattern fPatternUp, fPatternDown, fPatternLeft, fPatternRight;

View File

@ -43,6 +43,7 @@
#include <Alert.h>
#include <SupportDefs.h>
#include <Screen.h>
#include <Roster.h>
#include "ShowImageConstants.h"
#include "ShowImageWindow.h"
@ -110,7 +111,14 @@ ShowImageWindow::ShowImageWindow(const entry_ref *pref)
SetPulseRate(100000); // every 1/10 second; ShowImageView needs it for marching ants
Show();
if (InitCheck() == B_OK) {
fpImageView->FlushToLeftTop();
WindowRedimension(fpImageView->GetBitmap());
Show();
} else {
// exit if file could not be opened
Quit();
}
}
ShowImageWindow::~ShowImageWindow()
@ -121,7 +129,7 @@ ShowImageWindow::~ShowImageWindow()
status_t
ShowImageWindow::InitCheck()
{
if (!fpRef || !fpImageView)
if (!fpRef || !fpImageView || fpImageView->GetBitmap() == NULL)
return B_ERROR;
else
return B_OK;
@ -148,11 +156,40 @@ ShowImageWindow::UpdateTitle()
}
}
void
ShowImageWindow::UpdateRecentDocumentsMenu()
{
BMenuItem *item;
BMessage list, *msg;
entry_ref ref;
char name[B_FILE_NAME_LENGTH];
while ((item = fpOpenMenu->RemoveItem((int32)0)) != NULL) {
delete item;
}
be_roster->GetRecentDocuments(&list, 20, NULL, APP_SIG);
for (int i = 0; list.FindRef("refs", i, &ref) == B_OK; i++) {
BEntry entry(&ref);
entry.GetName(name);
msg = new BMessage(B_REFS_RECEIVED);
msg->AddRef("refs", &ref);
item = new BMenuItem(name, msg, 0, 0);
fpOpenMenu->AddItem(item);
item->SetTarget(be_app, NULL);
}
}
void
ShowImageWindow::LoadMenus(BMenuBar *pbar)
{
BMenu *pmenu = new BMenu("File");
AddItemMenu(pmenu, "Open", MSG_FILE_OPEN, 'O', 0, 'A', true);
fpOpenMenu = new BMenu("Open");
pmenu->AddItem(fpOpenMenu);
fpOpenMenu->Superitem()->SetTrigger('O');
fpOpenMenu->Superitem()->SetMessage(new BMessage(MSG_FILE_OPEN));
fpOpenMenu->Superitem()->SetTarget(be_app);
fpOpenMenu->Superitem()->SetShortcut('O', 0);
pmenu->AddSeparatorItem();
AddItemMenu(pmenu, "Dia Show", MSG_DIA_SHOW, 0, 0, 'W', true);
BMenu* pDelay = new BMenu("Delay");
@ -193,11 +230,11 @@ ShowImageWindow::LoadMenus(BMenuBar *pbar)
AddItemMenu(pmenu, "Undo", B_UNDO, 'Z', 0, 'W', false);
pmenu->AddSeparatorItem();
AddItemMenu(pmenu, "Cut", B_CUT, 'X', 0, 'W', false);
AddItemMenu(pmenu, "Copy", B_COPY, 'C', 0, 'W', false);
AddItemMenu(pmenu, "Copy", B_COPY, 'C', 0, 'W', true);
AddItemMenu(pmenu, "Paste", B_PASTE, 'V', 0, 'W', false);
AddItemMenu(pmenu, "Clear", MSG_CLEAR_SELECT, 0, 0, 'W', false);
AddItemMenu(pmenu, "Clear", MSG_CLEAR_SELECT, 0, 0, 'W', true);
pmenu->AddSeparatorItem();
AddItemMenu(pmenu, "Select All", MSG_SELECT_ALL, 'A', 0, 'W', false);
AddItemMenu(pmenu, "Select All", MSG_SELECT_ALL, 'A', 0, 'W', true);
pbar->AddItem(pmenu);
pmenu = fpPageMenu = new BMenu("Page");
@ -223,6 +260,8 @@ ShowImageWindow::LoadMenus(BMenuBar *pbar)
AddItemMenu(pmenu, "Fit To Window Size", MSG_FIT_TO_WINDOW_SIZE, 0, 0, 'W', true);
AddItemMenu(pmenu, "Full Screen", MSG_FULL_SCREEN, B_ENTER, 0, 'W', true);
pbar->AddItem(pmenu);
UpdateRecentDocumentsMenu();
}
BMenuItem *
@ -260,32 +299,28 @@ ShowImageWindow::AddDelayItem(BMenu *pmenu, char *caption, float value, bool mar
void
ShowImageWindow::WindowRedimension(BBitmap *pbitmap)
{
// set the window's min & max size limits
// based on document's data bounds
float maxWidth = pbitmap->Bounds().Width() + B_V_SCROLL_BAR_WIDTH;
float maxHeight = pbitmap->Bounds().Height() + fpBar->Frame().Height() +
B_H_SCROLL_BAR_HEIGHT + 1;
float minWidth = min(maxWidth, 100.0f);
float minHeight = min(maxHeight, 100.0f);
BScreen screen;
BRect r(pbitmap->Bounds());
float width, height;
float maxWidth, maxHeight;
const float windowBorderWidth = 5;
const float windowBorderHeight = 5;
if (screen.Frame().right == 0.0) {
return; // invalid screen object
}
width = r.Width() + B_V_SCROLL_BAR_WIDTH;
height = r.Height() + 1 + fpBar->Frame().Height() + B_H_SCROLL_BAR_HEIGHT;
// adjust the window's current size based on new min/max values
float curWidth = Bounds().Width();
float curHeight = Bounds().Height();
if (curWidth < minWidth)
curWidth = minWidth;
else if (curWidth > maxWidth)
curWidth = maxWidth;
if (curHeight < minHeight)
curHeight = minHeight;
else if (curHeight > maxHeight)
curHeight = maxHeight;
if (minWidth < 250)
minWidth = 250;
SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
ResizeTo(curWidth, curHeight);
// dimensions so that window does not reach outside of screen
maxWidth = screen.Frame().Width() + 1 - windowBorderWidth - Frame().left;
maxHeight = screen.Frame().Height() + 1 - windowBorderHeight - Frame().top;
if (width > maxWidth) width = maxWidth;
if (height > maxHeight) height = maxHeight;
ResizeTo(width, height);
}
void
@ -357,12 +392,15 @@ ShowImageWindow::MessageReceived(BMessage *pmsg)
case B_CUT:
break;
case B_COPY:
fpImageView->CopySelectionToClipboard();
break;
case B_PASTE:
break;
case MSG_CLEAR_SELECT:
fpImageView->Unselect();
break;
case MSG_SELECT_ALL:
fpImageView->SelectAll();
break;
case MSG_PAGE_FIRST:
@ -434,6 +472,10 @@ ShowImageWindow::MessageReceived(BMessage *pmsg)
case MSG_FULL_SCREEN:
ToggleFullScreen();
break;
case MSG_UPDATE_RECENT_DOCUMENTS:
UpdateRecentDocumentsMenu();
break;
default:
BWindow::MessageReceived(pmsg);

View File

@ -65,7 +65,8 @@ private:
char target, bool enabled);
BMenuItem* AddDelayItem(BMenu *pmenu, char *caption, float value, bool marked);
void UpdateRecentDocumentsMenu();
bool ToggleMenuItem(uint32 what);
void SaveAs(BMessage *pmsg);
@ -78,6 +79,7 @@ private:
BFilePanel *fpSavePanel;
BMenuBar *fpBar;
BMenu *fpOpenMenu;
BMenu *fpPageMenu;
entry_ref *fpRef;
ShowImageView *fpImageView;