StyledEdit:Implemented StatusLine and R5-like ReadOnly mode

This work was done during GCI2012.

Fixes #3655

Signed-off-by: Siarzhuk Zharski <zharik@gmx.li>
This commit is contained in:
Vlad Slepukhin 2012-12-22 22:51:08 +01:00 committed by Siarzhuk Zharski
parent 132b08d8ea
commit 0f4985d884
8 changed files with 446 additions and 13 deletions

View File

@ -78,8 +78,9 @@ const uint32 OPEN_AS_ENCODING = 'FPoe';
const uint32 SAVE_AS_ENCODING = 'FPse';
const uint32 SAVE_THEN_QUIT = 'FPsq';
// Update Line Info
const uint32 UPDATE_LINE = 'UPln';
// Update StatusView
const uint32 UPDATE_STATUS = 'UPSt';
const uint32 UNLOCK_FILE = 'UNLk';
#endif // CONSTANTS_H

View File

@ -13,6 +13,7 @@ Application StyledEdit :
ColorMenuItem.cpp
FindWindow.cpp
ReplaceWindow.cpp
StatusView.cpp
StyledEditApp.cpp
StyledEditView.cpp
StyledEditWindow.cpp
@ -26,6 +27,7 @@ DoCatalogs StyledEdit :
:
FindWindow.cpp
ReplaceWindow.cpp
StatusView.cpp
StyledEditApp.cpp
StyledEditWindow.cpp
;

View File

@ -0,0 +1,257 @@
/*
* Copyright 2002-2012, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Vlad Slepukhin
* Siarzhuk Zharski
*/
#include "StatusView.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Catalog.h>
#include <CharacterSet.h>
#include <CharacterSetRoster.h>
#include <ControlLook.h>
#include <MenuItem.h>
#include <Message.h>
#include <PopUpMenu.h>
#include <ScrollView.h>
#include <StringView.h>
#include <Window.h>
#include "Constants.h"
const float kHorzSpacing = 5.f;
#define UTF8_EXPAND_ARROW "\xe2\x96\xbe"
using namespace BPrivate;
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "StatusView"
StatusView::StatusView(BScrollView* scrollView)
:
BView(BRect(), "statusview",
B_FOLLOW_BOTTOM | B_FOLLOW_LEFT, B_WILL_DRAW),
fScrollView(scrollView),
fPreferredSize(0., 0.),
fReadOnly(false)
{
memset(fCellWidth, 0, sizeof(fCellWidth));
}
StatusView::~StatusView()
{
}
void
StatusView::AttachedToWindow()
{
SetFont(be_plain_font);
SetFontSize(10.);
BMessage message(UPDATE_STATUS);
message.AddInt32("line", 1);
message.AddInt32("column", 1);
message.AddString("encoding", "");
SetStatus(&message);
BScrollBar* scrollBar = fScrollView->ScrollBar(B_HORIZONTAL);
MoveTo(0., scrollBar->Frame().top);
rgb_color color = B_TRANSPARENT_COLOR;
BView* parent = Parent();
if (parent != NULL)
color = parent->ViewColor();
if (color == B_TRANSPARENT_COLOR)
color = ui_color(B_PANEL_BACKGROUND_COLOR);
SetViewColor(color);
ResizeToPreferred();
}
void
StatusView::GetPreferredSize(float* _width, float* _height)
{
_ValidatePreferredSize();
if (_width)
*_width = fPreferredSize.width;
if (_height)
*_height = fPreferredSize.height;
}
void
StatusView::ResizeToPreferred()
{
float width, height;
GetPreferredSize(&width, &height);
if (Bounds().Width() > width)
width = Bounds().Width();
BView::ResizeTo(width, height);
}
void
StatusView::Draw(BRect updateRect)
{
if (fPreferredSize.width <= 0)
return;
if (be_control_look != NULL) {
BRect bounds(Bounds());
be_control_look->DrawMenuBarBackground(this,
bounds, updateRect, ViewColor());
}
BRect bounds(Bounds());
rgb_color highColor = HighColor();
SetHighColor(tint_color(ViewColor(), B_DARKEN_2_TINT));
StrokeLine(bounds.LeftTop(), bounds.RightTop());
float x = bounds.left;
for (size_t i = 0; i < kStatusCellCount - 1; i++) {
x += fCellWidth[i];
StrokeLine(BPoint(x, bounds.top + 3), BPoint(x, bounds.bottom - 3));
}
SetLowColor(ViewColor());
SetHighColor(highColor);
font_height fontHeight;
GetFontHeight(&fontHeight);
x = bounds.left;
float y = (bounds.bottom + bounds.top
+ ceilf(fontHeight.ascent) - ceilf(fontHeight.descent)) / 2;
for (size_t i = 0; i < kStatusCellCount; i++) {
if (fCellText[i].Length() == 0)
continue;
DrawString(fCellText[i], BPoint(x + kHorzSpacing, y));
x += fCellWidth[i];
}
}
void
StatusView::MouseDown(BPoint where)
{
if (!fReadOnly)
return;
float left = fCellWidth[kPositionCell] + fCellWidth[kEncodingCell];
if (where.x < left)
return;
where.x = left;
where.y = Bounds().bottom;
BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
menu->AddItem(new BMenuItem(B_TRANSLATE("Unlock file"),
new BMessage(UNLOCK_FILE)));
ConvertToScreen(&where);
menu->SetTargetForItems(this);
menu->Go(where, true, true, true);
}
void
StatusView::SetStatus(BMessage* message)
{
int32 line = 0, column = 0;
if (B_OK == message->FindInt32("line", &line)
&& B_OK == message->FindInt32("column", &column))
{
char info[256];
snprintf(info, sizeof(info),
B_TRANSLATE("line %d, column %d"), line, column);
fCellText[kPositionCell].SetTo(info);
}
BString encoding;
if (B_OK == message->FindString("encoding", &encoding)) {
// sometime corresponding Int-32 "encoding" attrib is read as string :(
if (encoding.Length() == 0
|| encoding.Compare("\xff\xff") == 0
|| encoding.Compare("UTF-8") == 0)
{
fCellText[kEncodingCell] = "UTF-8";
} else {
const BCharacterSet* charset
= BCharacterSetRoster::FindCharacterSetByName(encoding);
fCellText[kEncodingCell]
= charset != NULL ? charset->GetPrintName() : "";
}
}
bool modified = false;
fReadOnly = false;
if (B_OK == message->FindBool("modified", &modified) && modified) {
fCellText[kFileStateCell] = B_TRANSLATE("Modified");
} else if (B_OK == message->FindBool("readOnly", &fReadOnly) && fReadOnly) {
fCellText[kFileStateCell] = B_TRANSLATE("Read-only");
fCellText[kFileStateCell] << " " UTF8_EXPAND_ARROW;
} else
fCellText[kFileStateCell].Truncate(0);
_ValidatePreferredSize();
Invalidate();
}
void
StatusView::_ValidatePreferredSize()
{
float orgWidth = fPreferredSize.width;
// width
fPreferredSize.width = 0.f;
for (size_t i = 0; i < kStatusCellCount; i++) {
if (fCellText[i].Length() == 0) {
fCellWidth[i] = 0;
continue;
}
float width = ceilf(StringWidth(fCellText[i]));
if (width > 0)
width += kHorzSpacing * 2;
if (width > fCellWidth[i])
fCellWidth[i] = width;
fPreferredSize.width += fCellWidth[i];
}
// height
font_height fontHeight;
GetFontHeight(&fontHeight);
fPreferredSize.height = ceilf(fontHeight.ascent + fontHeight.descent
+ fontHeight.leading);
if (fPreferredSize.height < B_H_SCROLL_BAR_HEIGHT)
fPreferredSize.height = B_H_SCROLL_BAR_HEIGHT;
float delta = fPreferredSize.width - orgWidth;
ResizeBy(delta, 0);
BScrollBar* scrollBar = fScrollView->ScrollBar(B_HORIZONTAL);
scrollBar->ResizeBy(-delta, 0);
scrollBar->MoveBy(delta, 0);
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2012, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Vlad Slepukhin
* Siarzhuk Zharski
*/
#ifndef STATUS_VIEW_H
#define STATUS_VIEW_H
#include <String.h>
#include <View.h>
enum {
kPositionCell,
kEncodingCell,
kFileStateCell,
kStatusCellCount
};
class BScrollView;
class StatusView : public BView {
public:
StatusView(BScrollView* fScrollView);
~StatusView();
void SetStatus(BMessage* mesage);
virtual void AttachedToWindow();
virtual void GetPreferredSize(float* _width, float* _height);
virtual void ResizeToPreferred();
virtual void Draw(BRect bounds);
virtual void MouseDown(BPoint point);
private:
void _ValidatePreferredSize();
private:
BScrollView* fScrollView;
BSize fPreferredSize;
BString fCellText[kStatusCellCount];
float fCellWidth[kStatusCellCount];
bool fReadOnly;
};
#endif // STATUS_VIEW_H

View File

@ -50,8 +50,8 @@ void
StyledEditView::Select(int32 start, int32 finish)
{
fMessenger->SendMessage(start == finish ? DISABLE_ITEMS : ENABLE_ITEMS);
fMessenger->SendMessage(UPDATE_LINE);
BTextView::Select(start, finish);
BTextView::Select(start, finish);
_UpdateStatus();
}
@ -181,7 +181,7 @@ StyledEditView::DeleteText(int32 start, int32 finish)
fMessenger-> SendMessage(TEXT_CHANGED);
BTextView::DeleteText(start, finish);
fMessenger->SendMessage(UPDATE_LINE);
_UpdateStatus();
}
@ -193,7 +193,7 @@ StyledEditView::InsertText(const char* text, int32 length, int32 offset,
fMessenger->SendMessage(TEXT_CHANGED);
BTextView::InsertText(text, length, offset, runs);
fMessenger->SendMessage(UPDATE_LINE);
_UpdateStatus();
}
@ -209,5 +209,34 @@ StyledEditView::FrameResized(float width, float height)
textRect.InsetBy(TEXT_INSET, TEXT_INSET);
SetTextRect(textRect);
}
}
}
void
StyledEditView::_UpdateStatus()
{
int32 selStart, selFinish;
GetSelection(&selStart, &selFinish);
int32 line = CurrentLine();
int32 lineStart = OffsetAt(line);
int32 column = 1;
int32 tabSize = (int32)ceilf(TabWidth() / StringWidth("s"));
for (int i = lineStart; i < selStart; i++) {
unsigned char ch = ByteAt(i);
if ((ch & 0xC0) != 0x80) {
if (ch == '\t')
while (column % tabSize)
column++;
column++;
}
}
BMessage* message = new BMessage(UPDATE_STATUS);
message->AddInt32("line", line + 1);
message->AddInt32("column", column);
message->AddString("encoding", fEncoding.String());
fMessenger->SendMessage(message);
}

View File

@ -42,6 +42,8 @@ class StyledEditView : public BTextView {
uint32 GetEncoding() const;
private:
void _UpdateStatus();
BMessenger *fMessenger;
bool fSuppressChanges;
BString fEncoding;

View File

@ -13,10 +13,11 @@
*/
#include "Constants.h"
#include "ColorMenuItem.h"
#include "Constants.h"
#include "FindWindow.h"
#include "ReplaceWindow.h"
#include "StatusView.h"
#include "StyledEditApp.h"
#include "StyledEditView.h"
#include "StyledEditWindow.h"
@ -514,6 +515,26 @@ StyledEditWindow::MessageReceived(BMessage* message)
}
break;
case UPDATE_STATUS:
message->AddBool("modified", !fClean);
message->AddBool("readOnly", !fTextView->IsEditable());
fStatusView->SetStatus(message);
break;
case UNLOCK_FILE:
{
status_t status = _UnlockFile();
if (status != B_OK) {
BString text;
bs_printf(&text,
B_TRANSLATE("Unable to unlock file\n\t%s"),
strerror(status));
_ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
}
PostMessage(UPDATE_STATUS);
break;
}
default:
BWindow::MessageReceived(message);
break;
@ -1058,6 +1079,9 @@ StyledEditWindow::_InitWindow(uint32 encoding)
AddChild(fScrollView);
fTextView->MakeFocus(true);
fStatusView = new StatusView(fScrollView);
fScrollView->AddChild(fStatusView);
// Add "File"-menu:
BMenu* menu = new BMenu(B_TRANSLATE("File"));
fMenuBar->AddItem(menu);
@ -1137,7 +1161,7 @@ StyledEditWindow::_InitWindow(uint32 encoding)
menu->AddItem(new BMenuItem(B_TRANSLATE("Find selection"),
new BMessage(MENU_FIND_SELECTION), 'H'));
menu->AddItem(new BMenuItem(B_TRANSLATE("Replace" B_UTF8_ELLIPSIS),
menu->AddItem(fReplaceItem = new BMenuItem(B_TRANSLATE("Replace" B_UTF8_ELLIPSIS),
new BMessage(MENU_REPLACE), 'R'));
menu->AddItem(fReplaceSameItem = new BMenuItem(B_TRANSLATE("Replace next"),
new BMessage(MENU_REPLACE_SAME), 'T'));
@ -1369,6 +1393,14 @@ StyledEditWindow::_LoadFile(entry_ref* ref, const char* forceEncoding)
return status;
}
struct stat st;
if (file.InitCheck() == B_OK && file.GetStat(&st) == B_OK) {
bool editable = (getuid() == st.st_uid && S_IWUSR & st.st_mode)
|| (getgid() == st.st_gid && S_IWGRP & st.st_mode)
|| (S_IWOTH & st.st_mode);
_SetReadOnly(!editable);
}
// update alignment
switch (fTextView->Alignment()) {
case B_ALIGN_LEFT:
@ -1480,6 +1512,50 @@ StyledEditWindow::_ReloadDocument(BMessage* message)
}
status_t
StyledEditWindow::_UnlockFile()
{
_NodeMonitorSuspender nodeMonitorSuspender(this);
if (!fSaveMessage)
return B_ERROR;
entry_ref dirRef;
const char* name;
if (fSaveMessage->FindRef("directory", &dirRef) != B_OK
|| fSaveMessage->FindString("name", &name) != B_OK)
return B_BAD_VALUE;
BDirectory dir(&dirRef);
BEntry entry(&dir, name);
status_t status = dir.InitCheck();
if (status != B_OK)
return status;
status = entry.InitCheck();
if (status != B_OK)
return status;
struct stat st;
BFile file(&entry, B_READ_WRITE);
status = file.InitCheck();
if (status != B_OK)
return status;
status = file.GetStat(&st);
if (status != B_OK)
return status;
st.st_mode |= S_IWUSR;
status = file.SetPermissions(st.st_mode);
if (status == B_OK)
_SetReadOnly(false);
return status;
}
bool
StyledEditWindow::_Search(BString string, bool caseSensitive, bool wrap,
bool backSearch, bool scrollToOccurence)
@ -1719,6 +1795,18 @@ StyledEditWindow::_ShowStatistics()
}
void
StyledEditWindow::_SetReadOnly(bool readOnly)
{
fReplaceItem->SetEnabled(!readOnly);
fReplaceSameItem->SetEnabled(!readOnly);
fFontMenu->SetEnabled(!readOnly);
fAlignLeft->Menu()->SetEnabled(!readOnly);
fWrapItem->SetEnabled(!readOnly);
fTextView->MakeEditable(!readOnly);
}
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Menus"
@ -1845,7 +1933,8 @@ StyledEditWindow::_HandleNodeMonitorEvent(BMessage *message)
{
int32 fields = 0;
if (message->FindInt32("fields", &fields) == B_OK
&& (fields & (B_STAT_SIZE | B_STAT_MODIFICATION_TIME)) == 0)
&& (fields & (B_STAT_SIZE | B_STAT_MODIFICATION_TIME
| B_STAT_MODE)) == 0)
break;
const char* name = NULL;

View File

@ -12,10 +12,8 @@
#include <Alert.h>
#include <Window.h>
#include <String.h>
#include <Message.h>
#include <Node.h>
#include <Window.h>
struct entry_ref;
@ -25,6 +23,7 @@ class BMenuBar;
class BMenuItem;
class BMessage;
class BScrollView;
class StatusView;
class StyledEditView;
@ -58,6 +57,7 @@ private:
status_t _LoadFile(entry_ref* ref,
const char* forceEncoding = NULL);
void _ReloadDocument(BMessage *message);
status_t _UnlockFile();
bool _Search(BString searchFor, bool caseSensitive,
bool wrap, bool backSearch,
bool scrollToOccurence = true);
@ -72,6 +72,7 @@ private:
void _SetFontStyle(const char* fontFamily,
const char* fontStyle);
int32 _ShowStatistics();
void _SetReadOnly(bool editable);
void _UpdateCleanUndoRedoSaveRevert();
int32 _ShowAlert(const BString& text,
const BString& label, const BString& label2,
@ -119,6 +120,7 @@ private:
BMenuItem* fCopyItem;
BMenuItem* fFindAgainItem;
BMenuItem* fReplaceItem;
BMenuItem* fReplaceSameItem;
BMenuItem* fBlackItem;
@ -160,6 +162,7 @@ private:
StyledEditView* fTextView;
BScrollView* fScrollView;
StatusView* fStatusView;
BFilePanel* fSavePanel;
BMenu* fSavePanelEncodingMenu;