1 /* 2 * Copyright 2006-2009, Stephan Aßmus <superstippi@gmx.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include "PadView.h" 7 8 #include <stdio.h> 9 10 #include <Application.h> 11 #include <Catalog.h> 12 #include <GroupLayout.h> 13 #include <MenuItem.h> 14 #include <Message.h> 15 #include <PopUpMenu.h> 16 #include <Region.h> 17 #include <Screen.h> 18 #include <SpaceLayoutItem.h> 19 20 #include "LaunchButton.h" 21 #include "MainWindow.h" 22 23 24 #undef B_TRANSLATION_CONTEXT 25 #define B_TRANSLATION_CONTEXT "LaunchBox" 26 27 28 static bigtime_t sActivationDelay = 40000; 29 static const uint32 kIconSizes[] = { 16, 20, 24, 32, 40, 48, 64 }; 30 31 32 enum { 33 MSG_TOGGLE_LAYOUT = 'tgll', 34 MSG_SET_ICON_SIZE = 'stis', 35 MSG_SET_IGNORE_DOUBLECLICK = 'strd' 36 }; 37 38 39 PadView::PadView(const char* name) 40 : BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, NULL), 41 fDragging(false), 42 fClickTime(0), 43 fButtonLayout(new BGroupLayout(B_VERTICAL, 4)), 44 fIconSize(DEFAULT_ICON_SIZE) 45 { 46 SetViewColor(B_TRANSPARENT_32_BIT); 47 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 48 get_click_speed(&sActivationDelay); 49 50 fButtonLayout->SetInsets(2, 7, 2, 2); 51 SetLayout(fButtonLayout); 52 } 53 54 55 PadView::~PadView() 56 { 57 } 58 59 60 void 61 PadView::Draw(BRect updateRect) 62 { 63 rgb_color background = LowColor(); 64 rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT); 65 rgb_color shadow = tint_color(background, B_DARKEN_2_TINT); 66 BRect r(Bounds()); 67 BeginLineArray(4); 68 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), light); 69 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), light); 70 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), shadow); 71 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), shadow); 72 EndLineArray(); 73 r.InsetBy(1.0, 1.0); 74 StrokeRect(r, B_SOLID_LOW); 75 r.InsetBy(1.0, 1.0); 76 // dots along top 77 BPoint dot = r.LeftTop(); 78 int32 current; 79 int32 stop; 80 BPoint offset; 81 BPoint next; 82 if (Orientation() == B_VERTICAL) { 83 current = (int32)dot.x; 84 stop = (int32)r.right; 85 offset = BPoint(0, 1); 86 next = BPoint(1, -4); 87 r.top += 5.0; 88 } else { 89 current = (int32)dot.y; 90 stop = (int32)r.bottom; 91 offset = BPoint(1, 0); 92 next = BPoint(-4, 1); 93 r.left += 5.0; 94 } 95 int32 num = 1; 96 while (current <= stop) { 97 rgb_color col1; 98 rgb_color col2; 99 if (num == 1) { 100 col1 = shadow; 101 col2 = background; 102 } else if (num == 2) { 103 col1 = background; 104 col2 = light; 105 } else { 106 col1 = background; 107 col2 = background; 108 num = 0; 109 } 110 SetHighColor(col1); 111 StrokeLine(dot, dot, B_SOLID_HIGH); 112 SetHighColor(col2); 113 dot += offset; 114 StrokeLine(dot, dot, B_SOLID_HIGH); 115 dot += offset; 116 StrokeLine(dot, dot, B_SOLID_LOW); 117 dot += offset; 118 SetHighColor(col1); 119 StrokeLine(dot, dot, B_SOLID_HIGH); 120 dot += offset; 121 SetHighColor(col2); 122 StrokeLine(dot, dot, B_SOLID_HIGH); 123 // next pixel 124 num++; 125 dot += next; 126 current++; 127 } 128 FillRect(r, B_SOLID_LOW); 129 } 130 131 132 void 133 PadView::MessageReceived(BMessage* message) 134 { 135 switch (message->what) { 136 case MSG_TOGGLE_LAYOUT: 137 if (fButtonLayout->Orientation() == B_HORIZONTAL) { 138 fButtonLayout->SetInsets(2, 7, 2, 2); 139 fButtonLayout->SetOrientation(B_VERTICAL); 140 } else { 141 fButtonLayout->SetInsets(7, 2, 2, 2); 142 fButtonLayout->SetOrientation(B_HORIZONTAL); 143 } 144 break; 145 146 case MSG_SET_ICON_SIZE: 147 uint32 size; 148 if (message->FindInt32("size", (int32*)&size) == B_OK) 149 SetIconSize(size); 150 break; 151 152 case MSG_SET_IGNORE_DOUBLECLICK: 153 SetIgnoreDoubleClick(!IgnoreDoubleClick()); 154 break; 155 156 default: 157 BView::MessageReceived(message); 158 break; 159 } 160 } 161 162 163 void 164 PadView::MouseDown(BPoint where) 165 { 166 BWindow* window = Window(); 167 if (window == NULL) 168 return; 169 170 BRegion region; 171 GetClippingRegion(®ion); 172 if (!region.Contains(where)) 173 return; 174 175 bool handle = true; 176 for (int32 i = 0; BView* child = ChildAt(i); i++) { 177 if (child->Frame().Contains(where)) { 178 handle = false; 179 break; 180 } 181 } 182 if (!handle) 183 return; 184 185 BMessage* message = window->CurrentMessage(); 186 if (message == NULL) 187 return; 188 189 uint32 buttons; 190 message->FindInt32("buttons", (int32*)&buttons); 191 if (buttons & B_SECONDARY_MOUSE_BUTTON) { 192 BRect r = Bounds(); 193 r.InsetBy(2.0, 2.0); 194 r.top += 6.0; 195 if (r.Contains(where)) { 196 DisplayMenu(where); 197 } else { 198 // sends the window to the back 199 window->Activate(false); 200 } 201 } else { 202 if (system_time() - fClickTime < sActivationDelay) { 203 window->Minimize(true); 204 fClickTime = 0; 205 } else { 206 window->Activate(); 207 fDragOffset = ConvertToScreen(where) - window->Frame().LeftTop(); 208 fDragging = true; 209 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 210 fClickTime = system_time(); 211 } 212 } 213 } 214 215 216 void 217 PadView::MouseUp(BPoint where) 218 { 219 if (BWindow* window = Window()) { 220 uint32 buttons; 221 window->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 222 if (buttons & B_PRIMARY_MOUSE_BUTTON 223 && system_time() - fClickTime < sActivationDelay 224 && window->IsActive()) 225 window->Activate(); 226 } 227 fDragging = false; 228 } 229 230 231 void 232 PadView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 233 { 234 MainWindow* window = dynamic_cast<MainWindow*>(Window()); 235 if (window == NULL) 236 return; 237 238 if (fDragging) { 239 window->MoveTo(ConvertToScreen(where) - fDragOffset); 240 } else if (window->AutoRaise()) { 241 where = ConvertToScreen(where); 242 BScreen screen(window); 243 BRect frame = screen.Frame(); 244 BRect windowFrame = window->Frame(); 245 if (where.x == frame.left || where.x == frame.right 246 || where.y == frame.top || where.y == frame.bottom) { 247 BPoint position = window->ScreenPosition(); 248 bool raise = false; 249 if (fabs(0.5 - position.x) > fabs(0.5 - position.y)) { 250 // left or right border 251 if (where.y >= windowFrame.top 252 && where.y <= windowFrame.bottom) { 253 if (position.x < 0.5 && where.x == frame.left) 254 raise = true; 255 else if (position.x > 0.5 && where.x == frame.right) 256 raise = true; 257 } 258 } else { 259 // top or bottom border 260 if (where.x >= windowFrame.left && where.x <= windowFrame.right) { 261 if (position.y < 0.5 && where.y == frame.top) 262 raise = true; 263 else if (position.y > 0.5 && where.y == frame.bottom) 264 raise = true; 265 } 266 } 267 if (raise) 268 window->Activate(); 269 } 270 } 271 } 272 273 274 void 275 PadView::AddButton(LaunchButton* button, LaunchButton* beforeButton) 276 { 277 button->SetIconSize(fIconSize); 278 279 if (beforeButton) 280 fButtonLayout->AddView(fButtonLayout->IndexOfView(beforeButton), button); 281 else 282 fButtonLayout->AddView(button); 283 284 _NotifySettingsChanged(); 285 } 286 287 288 bool 289 PadView::RemoveButton(LaunchButton* button) 290 { 291 bool result = fButtonLayout->RemoveView(button); 292 if (result) 293 _NotifySettingsChanged(); 294 return result; 295 } 296 297 298 LaunchButton* 299 PadView::ButtonAt(int32 index) const 300 { 301 BLayoutItem* item = fButtonLayout->ItemAt(index); 302 if (item == NULL) 303 return NULL; 304 return dynamic_cast<LaunchButton*>(item->View()); 305 } 306 307 308 void 309 PadView::DisplayMenu(BPoint where, LaunchButton* button) const 310 { 311 MainWindow* window = dynamic_cast<MainWindow*>(Window()); 312 if (window == NULL) 313 return; 314 315 LaunchButton* nearestButton = button; 316 if (!nearestButton) { 317 // find the nearest button 318 for (int32 i = 0; (nearestButton = ButtonAt(i)); i++) { 319 if (nearestButton->Frame().top > where.y) 320 break; 321 } 322 } 323 BPopUpMenu* menu = new BPopUpMenu(B_TRANSLATE("launch popup"), false, false); 324 // add button 325 BMessage* message = new BMessage(MSG_ADD_SLOT); 326 message->AddPointer("be:source", (void*)nearestButton); 327 BMenuItem* item = new BMenuItem(B_TRANSLATE("Add button here"), message); 328 item->SetTarget(window); 329 menu->AddItem(item); 330 // button options 331 if (button) { 332 // clear button 333 message = new BMessage(MSG_CLEAR_SLOT); 334 message->AddPointer("be:source", (void*)button); 335 item = new BMenuItem(B_TRANSLATE("Clear button"), message); 336 item->SetTarget(window); 337 menu->AddItem(item); 338 // remove button 339 message = new BMessage(MSG_REMOVE_SLOT); 340 message->AddPointer("be:source", (void*)button); 341 item = new BMenuItem(B_TRANSLATE("Remove button"), message); 342 item->SetTarget(window); 343 menu->AddItem(item); 344 // Open containing folder button 345 if (button->Ref() != NULL) { 346 message = new BMessage(MSG_OPEN_CONTAINING_FOLDER); 347 message->AddPointer("be:source", (void*)button); 348 item = new BMenuItem(B_TRANSLATE("Open containing folder"), message); 349 item->SetTarget(window); 350 menu->AddItem(item); 351 } 352 // set button description 353 if (button->Ref()) { 354 message = new BMessage(MSG_SET_DESCRIPTION); 355 message->AddPointer("be:source", (void*)button); 356 item = new BMenuItem(B_TRANSLATE("Set description"B_UTF8_ELLIPSIS), 357 message); 358 item->SetTarget(window); 359 menu->AddItem(item); 360 } 361 } 362 menu->AddSeparatorItem(); 363 // window settings 364 BMenu* settingsM = new BMenu(B_TRANSLATE("Settings")); 365 settingsM->SetFont(be_plain_font); 366 367 const char* toggleLayoutLabel; 368 if (fButtonLayout->Orientation() == B_HORIZONTAL) 369 toggleLayoutLabel = B_TRANSLATE("Vertical layout"); 370 else 371 toggleLayoutLabel = B_TRANSLATE("Horizontal layout"); 372 item = new BMenuItem(toggleLayoutLabel, new BMessage(MSG_TOGGLE_LAYOUT)); 373 item->SetTarget(this); 374 settingsM->AddItem(item); 375 376 BMenu* iconSizeM = new BMenu(B_TRANSLATE("Icon size")); 377 for (uint32 i = 0; i < sizeof(kIconSizes) / sizeof(uint32); i++) { 378 uint32 iconSize = kIconSizes[i]; 379 message = new BMessage(MSG_SET_ICON_SIZE); 380 message->AddInt32("size", iconSize); 381 BString label; 382 label << iconSize << " x " << iconSize; 383 item = new BMenuItem(label.String(), message); 384 item->SetTarget(this); 385 item->SetMarked(IconSize() == iconSize); 386 iconSizeM->AddItem(item); 387 } 388 settingsM->AddItem(iconSizeM); 389 390 item = new BMenuItem(B_TRANSLATE("Ignore double-click"), 391 new BMessage(MSG_SET_IGNORE_DOUBLECLICK)); 392 item->SetTarget(this); 393 item->SetMarked(IgnoreDoubleClick()); 394 settingsM->AddItem(item); 395 396 uint32 what = window->Look() == B_BORDERED_WINDOW_LOOK ? MSG_SHOW_BORDER : MSG_HIDE_BORDER; 397 item = new BMenuItem(B_TRANSLATE("Show window border"), new BMessage(what)); 398 item->SetTarget(window); 399 item->SetMarked(what == MSG_HIDE_BORDER); 400 settingsM->AddItem(item); 401 402 item = new BMenuItem(B_TRANSLATE("Auto-raise"), new BMessage(MSG_TOGGLE_AUTORAISE)); 403 item->SetTarget(window); 404 item->SetMarked(window->AutoRaise()); 405 settingsM->AddItem(item); 406 407 item = new BMenuItem(B_TRANSLATE("Show on all workspaces"), new BMessage(MSG_SHOW_ON_ALL_WORKSPACES)); 408 item->SetTarget(window); 409 item->SetMarked(window->ShowOnAllWorkspaces()); 410 settingsM->AddItem(item); 411 412 menu->AddItem(settingsM); 413 414 menu->AddSeparatorItem(); 415 416 // pad commands 417 BMenu* padM = new BMenu(B_TRANSLATE("Pad")); 418 padM->SetFont(be_plain_font); 419 // new pad 420 item = new BMenuItem(B_TRANSLATE("New"), new BMessage(MSG_ADD_WINDOW)); 421 item->SetTarget(be_app); 422 padM->AddItem(item); 423 // new pad 424 item = new BMenuItem(B_TRANSLATE("Clone"), new BMessage(MSG_ADD_WINDOW)); 425 item->SetTarget(window); 426 padM->AddItem(item); 427 padM->AddSeparatorItem(); 428 // close 429 item = new BMenuItem(B_TRANSLATE("Close"), new BMessage(B_QUIT_REQUESTED)); 430 item->SetTarget(window); 431 padM->AddItem(item); 432 menu->AddItem(padM); 433 // app commands 434 BMenu* appM = new BMenu(B_TRANSLATE_SYSTEM_NAME("LaunchBox")); 435 appM->SetFont(be_plain_font); 436 // quit 437 item = new BMenuItem(B_TRANSLATE("Quit"), new BMessage(B_QUIT_REQUESTED)); 438 item->SetTarget(be_app); 439 appM->AddItem(item); 440 menu->AddItem(appM); 441 // finish popup 442 menu->SetAsyncAutoDestruct(true); 443 menu->SetFont(be_plain_font); 444 where = ConvertToScreen(where); 445 BRect mouseRect(where, where); 446 mouseRect.InsetBy(-4.0, -4.0); 447 menu->Go(where, true, false, mouseRect, true); 448 } 449 450 451 void 452 PadView::SetOrientation(enum orientation orientation) 453 { 454 if (orientation == B_VERTICAL) { 455 fButtonLayout->SetInsets(2, 7, 2, 2); 456 fButtonLayout->SetOrientation(B_VERTICAL); 457 } else { 458 fButtonLayout->SetInsets(7, 2, 2, 2); 459 fButtonLayout->SetOrientation(B_HORIZONTAL); 460 } 461 _NotifySettingsChanged(); 462 } 463 464 465 enum orientation 466 PadView::Orientation() const 467 { 468 return fButtonLayout->Orientation(); 469 } 470 471 472 void 473 PadView::SetIconSize(uint32 size) 474 { 475 if (size == fIconSize) 476 return; 477 478 fIconSize = size; 479 480 for (int32 i = 0; LaunchButton* button = ButtonAt(i); i++) 481 button->SetIconSize(fIconSize); 482 483 _NotifySettingsChanged(); 484 } 485 486 487 uint32 488 PadView::IconSize() const 489 { 490 return fIconSize; 491 } 492 493 494 void 495 PadView::SetIgnoreDoubleClick(bool refuse) 496 { 497 LaunchButton::SetIgnoreDoubleClick(refuse); 498 499 _NotifySettingsChanged(); 500 } 501 502 503 bool 504 PadView::IgnoreDoubleClick() const 505 { 506 return LaunchButton::IgnoreDoubleClick(); 507 } 508 509 510 void 511 PadView::_NotifySettingsChanged() 512 { 513 be_app->PostMessage(MSG_SETTINGS_CHANGED); 514 } 515 516