diff --git a/src/apps/stylededit/Constants.h b/src/apps/stylededit/Constants.h index bebe360c40..9cd538167c 100644 --- a/src/apps/stylededit/Constants.h +++ b/src/apps/stylededit/Constants.h @@ -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 diff --git a/src/apps/stylededit/Jamfile b/src/apps/stylededit/Jamfile index 82e089d73e..1cd8e88651 100644 --- a/src/apps/stylededit/Jamfile +++ b/src/apps/stylededit/Jamfile @@ -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 ; diff --git a/src/apps/stylededit/StatusView.cpp b/src/apps/stylededit/StatusView.cpp new file mode 100644 index 0000000000..edd3d9878e --- /dev/null +++ b/src/apps/stylededit/StatusView.cpp @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} + diff --git a/src/apps/stylededit/StatusView.h b/src/apps/stylededit/StatusView.h new file mode 100644 index 0000000000..fcf542f303 --- /dev/null +++ b/src/apps/stylededit/StatusView.h @@ -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 +#include + + +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 diff --git a/src/apps/stylededit/StyledEditView.cpp b/src/apps/stylededit/StyledEditView.cpp index 166eb992be..9c8936919e 100644 --- a/src/apps/stylededit/StyledEditView.cpp +++ b/src/apps/stylededit/StyledEditView.cpp @@ -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); +} diff --git a/src/apps/stylededit/StyledEditView.h b/src/apps/stylededit/StyledEditView.h index 272f193bf2..951c0ead39 100644 --- a/src/apps/stylededit/StyledEditView.h +++ b/src/apps/stylededit/StyledEditView.h @@ -42,6 +42,8 @@ class StyledEditView : public BTextView { uint32 GetEncoding() const; private: + void _UpdateStatus(); + BMessenger *fMessenger; bool fSuppressChanges; BString fEncoding; diff --git a/src/apps/stylededit/StyledEditWindow.cpp b/src/apps/stylededit/StyledEditWindow.cpp index bf6675d180..9fa7428acc 100644 --- a/src/apps/stylededit/StyledEditWindow.cpp +++ b/src/apps/stylededit/StyledEditWindow.cpp @@ -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; diff --git a/src/apps/stylededit/StyledEditWindow.h b/src/apps/stylededit/StyledEditWindow.h index ad5db0fe2f..ae8aac3efa 100644 --- a/src/apps/stylededit/StyledEditWindow.h +++ b/src/apps/stylededit/StyledEditWindow.h @@ -12,10 +12,8 @@ #include -#include -#include -#include #include +#include 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;