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