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