1 /* 2 * Copyright 2002-2012, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Vlad Slepukhin 7 * Siarzhuk Zharski 8 */ 9 10 11 #include "StatusView.h" 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <Catalog.h> 18 #include <CharacterSet.h> 19 #include <CharacterSetRoster.h> 20 #include <ControlLook.h> 21 #include <MenuItem.h> 22 #include <Message.h> 23 #include <PopUpMenu.h> 24 #include <ScrollView.h> 25 #include <StringView.h> 26 #include <Window.h> 27 28 #include <tracker_private.h> 29 #include "DirMenu.h" 30 31 #include "Constants.h" 32 33 34 const float kHorzSpacing = 5.f; 35 #define UTF8_EXPAND_ARROW "\xe2\x96\xbe" 36 37 using namespace BPrivate; 38 39 40 #undef B_TRANSLATION_CONTEXT 41 #define B_TRANSLATION_CONTEXT "StatusView" 42 43 44 StatusView::StatusView(BScrollView* scrollView) 45 : 46 BView(BRect(), "statusview", 47 B_FOLLOW_BOTTOM | B_FOLLOW_LEFT, B_WILL_DRAW), 48 fScrollView(scrollView), 49 fPreferredSize(0., 0.), 50 fReadOnly(false), 51 fCanUnlock(false) 52 { 53 memset(fCellWidth, 0, sizeof(fCellWidth)); 54 } 55 56 57 StatusView::~StatusView() 58 { 59 } 60 61 62 void 63 StatusView::AttachedToWindow() 64 { 65 SetFont(be_plain_font); 66 SetFontSize(10.); 67 68 BMessage message(UPDATE_STATUS); 69 message.AddInt32("line", 1); 70 message.AddInt32("column", 1); 71 message.AddString("encoding", ""); 72 SetStatus(&message); 73 74 BScrollBar* scrollBar = fScrollView->ScrollBar(B_HORIZONTAL); 75 MoveTo(0., scrollBar->Frame().top); 76 77 rgb_color color = B_TRANSPARENT_COLOR; 78 BView* parent = Parent(); 79 if (parent != NULL) 80 color = parent->ViewColor(); 81 82 if (color == B_TRANSPARENT_COLOR) 83 color = ui_color(B_PANEL_BACKGROUND_COLOR); 84 85 SetViewColor(color); 86 87 ResizeToPreferred(); 88 } 89 90 91 void 92 StatusView::GetPreferredSize(float* _width, float* _height) 93 { 94 _ValidatePreferredSize(); 95 96 if (_width) 97 *_width = fPreferredSize.width; 98 99 if (_height) 100 *_height = fPreferredSize.height; 101 } 102 103 104 void 105 StatusView::ResizeToPreferred() 106 { 107 float width, height; 108 GetPreferredSize(&width, &height); 109 110 if (Bounds().Width() > width) 111 width = Bounds().Width(); 112 113 BView::ResizeTo(width, height); 114 } 115 116 117 void 118 StatusView::Draw(BRect updateRect) 119 { 120 if (fPreferredSize.width <= 0) 121 return; 122 123 if (be_control_look != NULL) { 124 BRect bounds(Bounds()); 125 be_control_look->DrawMenuBarBackground(this, 126 bounds, updateRect, ViewColor()); 127 } 128 129 BRect bounds(Bounds()); 130 rgb_color highColor = HighColor(); 131 SetHighColor(tint_color(ViewColor(), B_DARKEN_2_TINT)); 132 StrokeLine(bounds.LeftTop(), bounds.RightTop()); 133 134 float x = bounds.left; 135 for (size_t i = 0; i < kStatusCellCount - 1; i++) { 136 x += fCellWidth[i]; 137 StrokeLine(BPoint(x, bounds.top + 3), BPoint(x, bounds.bottom - 3)); 138 } 139 140 SetLowColor(ViewColor()); 141 SetHighColor(highColor); 142 143 font_height fontHeight; 144 GetFontHeight(&fontHeight); 145 146 x = bounds.left; 147 float y = (bounds.bottom + bounds.top 148 + ceilf(fontHeight.ascent) - ceilf(fontHeight.descent)) / 2; 149 150 for (size_t i = 0; i < kStatusCellCount; i++) { 151 if (fCellText[i].Length() == 0) 152 continue; 153 DrawString(fCellText[i], BPoint(x + kHorzSpacing, y)); 154 x += fCellWidth[i]; 155 } 156 } 157 158 159 void 160 StatusView::MouseDown(BPoint where) 161 { 162 if (where.x < fCellWidth[kPositionCell]) { 163 _ShowDirMenu(); 164 return; 165 } 166 167 if (!fReadOnly || !fCanUnlock) 168 return; 169 170 float left = fCellWidth[kPositionCell] + fCellWidth[kEncodingCell]; 171 if (where.x < left) 172 return; 173 174 int32 clicks = 0; 175 BMessage* message = Window()->CurrentMessage(); 176 if (message != NULL 177 && message->FindInt32("clicks", &clicks) == B_OK && clicks > 1) 178 return; 179 180 BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 181 menu->AddItem(new BMenuItem(B_TRANSLATE("Unlock file"), 182 new BMessage(UNLOCK_FILE))); 183 where.x = left; 184 where.y = Bounds().bottom; 185 186 ConvertToScreen(&where); 187 menu->SetTargetForItems(this); 188 menu->Go(where, true, true, true); 189 } 190 191 192 void 193 StatusView::SetStatus(BMessage* message) 194 { 195 int32 line = 0, column = 0; 196 if (B_OK == message->FindInt32("line", &line) 197 && B_OK == message->FindInt32("column", &column)) 198 { 199 char info[256]; 200 snprintf(info, sizeof(info), 201 B_TRANSLATE("line %d, column %d"), line, column); 202 fCellText[kPositionCell].SetTo(info); 203 } 204 205 if (B_OK == message->FindString("encoding", &fEncoding)) { 206 // sometime corresponding Int-32 "encoding" attrib is read as string :( 207 if (fEncoding.Length() == 0 208 || fEncoding.Compare("\xff\xff") == 0 209 || fEncoding.Compare("UTF-8") == 0) 210 { 211 // do not display default UTF-8 encoding 212 fCellText[kEncodingCell].Truncate(0); 213 fEncoding.Truncate(0); 214 } else { 215 const BCharacterSet* charset 216 = BCharacterSetRoster::FindCharacterSetByName(fEncoding); 217 fCellText[kEncodingCell] 218 = charset != NULL ? charset->GetPrintName() : ""; 219 } 220 } 221 222 bool modified = false; 223 fReadOnly = false; 224 fCanUnlock = false; 225 if (B_OK == message->FindBool("modified", &modified) && modified) { 226 fCellText[kFileStateCell] = B_TRANSLATE("Modified"); 227 } else if (B_OK == message->FindBool("readOnly", &fReadOnly) && fReadOnly) { 228 fCellText[kFileStateCell] = B_TRANSLATE("Read-only"); 229 if (B_OK == message->FindBool("canUnlock", &fCanUnlock) && fCanUnlock) 230 fCellText[kFileStateCell] << " " UTF8_EXPAND_ARROW; 231 } else 232 fCellText[kFileStateCell].Truncate(0); 233 234 _ValidatePreferredSize(); 235 Invalidate(); 236 } 237 238 239 void 240 StatusView::SetRef(const entry_ref& ref) 241 { 242 fRef = ref; 243 } 244 245 246 void 247 StatusView::_ValidatePreferredSize() 248 { 249 float orgWidth = fPreferredSize.width; 250 // width 251 fPreferredSize.width = 0.f; 252 for (size_t i = 0; i < kStatusCellCount; i++) { 253 if (fCellText[i].Length() == 0) { 254 fCellWidth[i] = 0; 255 continue; 256 } 257 float width = ceilf(StringWidth(fCellText[i])); 258 if (width > 0) 259 width += kHorzSpacing * 2; 260 if (width > fCellWidth[i] || i != kPositionCell) 261 fCellWidth[i] = width; 262 fPreferredSize.width += fCellWidth[i]; 263 } 264 265 // height 266 font_height fontHeight; 267 GetFontHeight(&fontHeight); 268 269 fPreferredSize.height = ceilf(fontHeight.ascent + fontHeight.descent 270 + fontHeight.leading); 271 272 if (fPreferredSize.height < B_H_SCROLL_BAR_HEIGHT) 273 fPreferredSize.height = B_H_SCROLL_BAR_HEIGHT; 274 275 float delta = fPreferredSize.width - orgWidth; 276 ResizeBy(delta, 0); 277 BScrollBar* scrollBar = fScrollView->ScrollBar(B_HORIZONTAL); 278 scrollBar->ResizeBy(-delta, 0); 279 scrollBar->MoveBy(delta, 0); 280 } 281 282 283 void 284 StatusView::_ShowDirMenu() 285 { 286 BEntry entry; 287 status_t status = entry.SetTo(&fRef); 288 289 if (status != B_OK || !entry.Exists()) 290 return; 291 292 BPrivate::BDirMenu* menu = new BDirMenu(NULL, 293 BMessenger(kTrackerSignature), B_REFS_RECEIVED); 294 295 menu->Populate(&entry, Window(), false, false, true, false, true); 296 297 BPoint point = Bounds().LeftBottom(); 298 point.y += 3; 299 ConvertToScreen(&point); 300 BRect clickToOpenRect(Bounds()); 301 ConvertToScreen(&clickToOpenRect); 302 menu->Go(point, true, true, clickToOpenRect); 303 delete menu; 304 } 305 306