1/* 2Open Tracker License 3 4Terms and Conditions 5 6Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8Permission is hereby granted, free of charge, to any person obtaining a copy of 9this software and associated documentation files (the "Software"), to deal in 10the Software without restriction, including without limitation the rights to 11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12of the Software, and to permit persons to whom the Software is furnished to do 13so, subject to the following conditions: 14 15The above copyright notice and this permission notice applies to all licensees 16and shall be included in all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25Except as contained in this notice, the name of Be Incorporated shall not be 26used in advertising or otherwise to promote the sale, use or other dealings in 27this Software without prior written authorization from Be Incorporated. 28 29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30of Be Incorporated in the United States and other countries. Other brand product 31names are registered trademarks or trademarks of their respective holders. 32All rights reserved. 33*/ 34 35// defines the status area drawn in the bottom left corner of a Tracker window 36 37 38#include "CountView.h" 39 40#include <Application.h> 41#include <Catalog.h> 42#include <ControlLook.h> 43#include <Locale.h> 44#include <StringFormat.h> 45 46#include "AutoLock.h" 47#include "Bitmaps.h" 48#include "ContainerWindow.h" 49#include "DirMenu.h" 50#include "PoseView.h" 51#include "Utilities.h" 52 53 54#undef B_TRANSLATION_CONTEXT 55#define B_TRANSLATION_CONTEXT "CountView" 56 57 58const bigtime_t kBarberPoleDelay = 500000; 59static const float kMinFontSize = 8.0f; 60 61 62// #pragma mark - BCountView 63 64 65BCountView::BCountView(BPoseView* view) 66 : 67 BView("CountVw", B_PULSE_NEEDED | B_WILL_DRAW), 68 fLastCount(-1), 69 fPoseView(view), 70 fShowingBarberPole(false), 71 fBarberPoleMap(NULL), 72 fLastBarberPoleOffset(5), 73 fStartSpinningAfter(0), 74 fTypeAheadString(""), 75 fFilterString("") 76{ 77 GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, 78 R_BarberPoleBitmap, &fBarberPoleMap); 79} 80 81 82BCountView::~BCountView() 83{ 84 delete fBarberPoleMap; 85} 86 87 88void 89BCountView::TrySpinningBarberPole() 90{ 91 if (!fShowingBarberPole) 92 return; 93 94 if (fStartSpinningAfter && system_time() < fStartSpinningAfter) 95 return; 96 97 // When the barber pole just starts spinning we need to invalidate 98 // the whole rectangle of text and barber pole. 99 // After this the text needs no updating since only the pole changes. 100 if (fStartSpinningAfter) { 101 fStartSpinningAfter = 0; 102 Invalidate(TextAndBarberPoleRect()); 103 } else 104 Invalidate(BarberPoleInnerRect()); 105} 106 107 108void 109BCountView::Pulse() 110{ 111 TrySpinningBarberPole(); 112} 113 114 115void 116BCountView::EndBarberPole() 117{ 118 if (!fShowingBarberPole) 119 return; 120 121 fShowingBarberPole = false; 122 Invalidate(); 123} 124 125 126void 127BCountView::StartBarberPole() 128{ 129 AutoLock<BWindow> lock(Window()); 130 if (fShowingBarberPole) 131 return; 132 133 fShowingBarberPole = true; 134 fStartSpinningAfter = system_time() + kBarberPoleDelay; 135 // wait a bit before showing the barber pole 136} 137 138 139BRect 140BCountView::BarberPoleInnerRect() const 141{ 142 BRect result = Bounds(); 143 result.InsetBy(3, 4); 144 result.left = result.right - 7; 145 result.bottom = result.top + 7; 146 return result; 147} 148 149 150BRect 151BCountView::BarberPoleOuterRect() const 152{ 153 BRect result(BarberPoleInnerRect()); 154 result.InsetBy(-1, -1); 155 return result; 156} 157 158 159BRect 160BCountView::TextInvalRect() const 161{ 162 BRect result = TextAndBarberPoleRect(); 163 164 // if the barber pole is not present, use its space for text 165 if (fShowingBarberPole) 166 result.right -= 10; 167 168 return result; 169} 170 171 172BRect 173BCountView::TextAndBarberPoleRect() const 174{ 175 BRect result = Bounds(); 176 result.InsetBy(be_control_look->ComposeSpacing(B_USE_SMALL_SPACING) / 2, 177 floorf(result.Height() * 0.25f)); 178 179 return result; 180} 181 182 183void 184BCountView::CheckCount() 185{ 186 // invalidate the count text area if necessary 187 if (fLastCount != fPoseView->CountItems()) { 188 fLastCount = fPoseView->CountItems(); 189 Invalidate(TextInvalRect()); 190 } 191 192 // invalidate barber pole area if necessary 193 TrySpinningBarberPole(); 194} 195 196 197void 198BCountView::Draw(BRect updateRect) 199{ 200 BRect bounds(Bounds()); 201 202 rgb_color color = ViewColor(); 203 if (IsTypingAhead()) 204 color = ui_color(B_DOCUMENT_BACKGROUND_COLOR); 205 206 SetLowColor(color); 207 be_control_look->DrawBorder(this, bounds, updateRect, 208 color, B_PLAIN_BORDER, 0, 209 BControlLook::B_BOTTOM_BORDER | BControlLook::B_LEFT_BORDER); 210 be_control_look->DrawMenuBarBackground(this, bounds, updateRect, color); 211 212 BString itemString; 213 if (IsTypingAhead()) 214 itemString << TypeAhead(); 215 else if (IsFiltering()) { 216 itemString << fLastCount << " " << Filter(); 217 } else { 218 if (fLastCount == 0) 219 itemString << B_TRANSLATE("no items"); 220 else { 221 static BStringFormat format(B_TRANSLATE_COMMENT( 222 "{0, plural, one{# item} other{# items}}", 223 "Number of selected items: \"1 item\" or \"2 items\"")); 224 format.Format(itemString, fLastCount); 225 } 226 } 227 228 BRect textRect(TextInvalRect()); 229 230 TruncateString(&itemString, IsTypingAhead() ? B_TRUNCATE_BEGINNING 231 : IsFiltering() ? B_TRUNCATE_MIDDLE : B_TRUNCATE_END, 232 textRect.Width()); 233 234 if (IsTypingAhead()) { 235 // use a muted gray for the typeahead 236 SetHighUIColor(B_DOCUMENT_TEXT_COLOR); 237 } else 238 SetHighUIColor(B_PANEL_TEXT_COLOR); 239 240 MovePenTo(textRect.LeftBottom()); 241 DrawString(itemString.String()); 242 243 bounds.top++; 244 245 rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT); 246 rgb_color shadow = tint_color(ViewColor(), B_DARKEN_2_TINT); 247 248 BeginLineArray(fShowingBarberPole && !fStartSpinningAfter ? 9 : 5); 249 250 if (!fShowingBarberPole || fStartSpinningAfter) { 251 EndLineArray(); 252 return; 253 } 254 255 BRect barberPoleRect(BarberPoleOuterRect()); 256 257 AddLine(barberPoleRect.LeftTop(), barberPoleRect.RightTop(), shadow); 258 AddLine(barberPoleRect.LeftTop(), barberPoleRect.LeftBottom(), shadow); 259 AddLine(barberPoleRect.LeftBottom(), barberPoleRect.RightBottom(), light); 260 AddLine(barberPoleRect.RightBottom(), barberPoleRect.RightTop(), light); 261 EndLineArray(); 262 263 barberPoleRect.InsetBy(1, 1); 264 265 BRect destRect(fBarberPoleMap 266 ? fBarberPoleMap->Bounds() : BRect(0, 0, 0, 0)); 267 destRect.OffsetTo(barberPoleRect.LeftTop() 268 - BPoint(0, fLastBarberPoleOffset)); 269 fLastBarberPoleOffset -= 1; 270 if (fLastBarberPoleOffset < 0) 271 fLastBarberPoleOffset = 5; 272 273 BRegion region; 274 region.Set(BarberPoleInnerRect()); 275 ConstrainClippingRegion(®ion); 276 277 if (fBarberPoleMap) 278 DrawBitmap(fBarberPoleMap, destRect); 279} 280 281 282void 283BCountView::MouseDown(BPoint) 284{ 285 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 286 ThrowOnAssert(window != NULL); 287 288 window->Activate(); 289 window->UpdateIfNeeded(); 290 291 if (fPoseView->IsFilePanel() || fPoseView->TargetModel() == NULL) 292 return; 293 294 if (!window->TargetModel()->IsRoot()) { 295 BDirMenu* menu = new BDirMenu(NULL, be_app, B_REFS_RECEIVED); 296 BEntry entry; 297 if (entry.SetTo(window->TargetModel()->EntryRef()) == B_OK) 298 menu->Populate(&entry, Window(), false, false, true, false, true); 299 else 300 menu->Populate(NULL, Window(), false, false, true, false, true); 301 302 BPoint point = Bounds().LeftBottom(); 303 point.y += 3; 304 ConvertToScreen(&point); 305 BRect clickToOpenRect(Bounds()); 306 ConvertToScreen(&clickToOpenRect); 307 menu->Go(point, true, true, clickToOpenRect); 308 delete menu; 309 } 310} 311 312 313void 314BCountView::AttachedToWindow() 315{ 316 SetFont(be_plain_font); 317 SetFontSize(std::max(kMinFontSize, 318 ceilf(be_plain_font->Size() * 0.75f))); 319 320 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 321 SetLowUIColor(ViewUIColor()); 322 323 CheckCount(); 324} 325 326 327void 328BCountView::SetTypeAhead(const char* string) 329{ 330 fTypeAheadString = string; 331 Invalidate(); 332} 333 334 335const char* 336BCountView::TypeAhead() const 337{ 338 return fTypeAheadString.String(); 339} 340 341 342bool 343BCountView::IsTypingAhead() const 344{ 345 return fTypeAheadString.Length() != 0; 346} 347 348 349void 350BCountView::AddFilterCharacter(const char* character) 351{ 352 fFilterString.AppendChars(character, 1); 353 Invalidate(); 354} 355 356 357void 358BCountView::RemoveFilterCharacter() 359{ 360 fFilterString.TruncateChars(fFilterString.CountChars() - 1); 361 Invalidate(); 362} 363 364 365void 366BCountView::CancelFilter() 367{ 368 fFilterString.Truncate(0); 369 Invalidate(); 370} 371 372 373const char* 374BCountView::Filter() const 375{ 376 return fFilterString.String(); 377} 378 379 380bool 381BCountView::IsFiltering() const 382{ 383 return fFilterString.Length() > 0; 384} 385