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 return dynamic_cast<LaunchButton*>(ChildAt(index)); 301 } 302 303 304 void 305 PadView::DisplayMenu(BPoint where, LaunchButton* button) const 306 { 307 MainWindow* window = dynamic_cast<MainWindow*>(Window()); 308 if (window == NULL) 309 return; 310 311 LaunchButton* nearestButton = button; 312 if (!nearestButton) { 313 // find the nearest button 314 for (int32 i = 0; (nearestButton = ButtonAt(i)); i++) { 315 if (nearestButton->Frame().top > where.y) 316 break; 317 } 318 } 319 BPopUpMenu* menu = new BPopUpMenu("launch popup", false, false); 320 // add button 321 BMessage* message = new BMessage(MSG_ADD_SLOT); 322 message->AddPointer("be:source", (void*)nearestButton); 323 BMenuItem* item = new BMenuItem("Add Button Here", message); 324 item->SetTarget(window); 325 menu->AddItem(item); 326 // button options 327 if (button) { 328 // clear button 329 message = new BMessage(MSG_CLEAR_SLOT); 330 message->AddPointer("be:source", (void*)button); 331 item = new BMenuItem("Clear Button", message); 332 item->SetTarget(window); 333 menu->AddItem(item); 334 // remove button 335 message = new BMessage(MSG_REMOVE_SLOT); 336 message->AddPointer("be:source", (void*)button); 337 item = new BMenuItem("Remove Button", message); 338 item->SetTarget(window); 339 menu->AddItem(item); 340 // set button description 341 if (button->Ref()) { 342 message = new BMessage(MSG_SET_DESCRIPTION); 343 message->AddPointer("be:source", (void*)button); 344 item = new BMenuItem("Set Description"B_UTF8_ELLIPSIS, message); 345 item->SetTarget(window); 346 menu->AddItem(item); 347 } 348 } 349 menu->AddSeparatorItem(); 350 // window settings 351 BMenu* settingsM = new BMenu("Settings"); 352 settingsM->SetFont(be_plain_font); 353 354 const char* toggleLayoutLabel; 355 if (fButtonLayout->Orientation() == B_HORIZONTAL) 356 toggleLayoutLabel = "Vertical Layout"; 357 else 358 toggleLayoutLabel = "Horizontal Layout"; 359 item = new BMenuItem(toggleLayoutLabel, new BMessage(MSG_TOGGLE_LAYOUT)); 360 item->SetTarget(this); 361 settingsM->AddItem(item); 362 363 BMenu* iconSizeM = new BMenu("Icon size"); 364 for (uint32 i = 0; i < sizeof(kIconSizes) / sizeof(uint32); i++) { 365 uint32 iconSize = kIconSizes[i]; 366 message = new BMessage(MSG_SET_ICON_SIZE); 367 message->AddInt32("size", iconSize); 368 BString label; 369 label << iconSize << " x " << iconSize; 370 item = new BMenuItem(label.String(), message); 371 item->SetTarget(this); 372 item->SetMarked(IconSize() == iconSize); 373 iconSizeM->AddItem(item); 374 } 375 settingsM->AddItem(iconSizeM); 376 377 item = new BMenuItem("Ignore Double-click", 378 new BMessage(MSG_SET_IGNORE_DOUBLECLICK)); 379 item->SetTarget(this); 380 item->SetMarked(IgnoreDoubleClick()); 381 settingsM->AddItem(item); 382 383 uint32 what = window->Look() == B_BORDERED_WINDOW_LOOK ? MSG_SHOW_BORDER : MSG_HIDE_BORDER; 384 item = new BMenuItem("Show Window Border", new BMessage(what)); 385 item->SetTarget(window); 386 item->SetMarked(what == MSG_HIDE_BORDER); 387 settingsM->AddItem(item); 388 389 item = new BMenuItem("Auto Raise", new BMessage(MSG_TOGGLE_AUTORAISE)); 390 item->SetTarget(window); 391 item->SetMarked(window->AutoRaise()); 392 settingsM->AddItem(item); 393 394 item = new BMenuItem("Show On All Workspaces", new BMessage(MSG_SHOW_ON_ALL_WORKSPACES)); 395 item->SetTarget(window); 396 item->SetMarked(window->ShowOnAllWorkspaces()); 397 settingsM->AddItem(item); 398 399 menu->AddItem(settingsM); 400 401 menu->AddSeparatorItem(); 402 403 // pad commands 404 BMenu* padM = new BMenu("Pad"); 405 padM->SetFont(be_plain_font); 406 // new pad 407 item = new BMenuItem("New", new BMessage(MSG_ADD_WINDOW)); 408 item->SetTarget(be_app); 409 padM->AddItem(item); 410 // new pad 411 item = new BMenuItem("Clone", new BMessage(MSG_ADD_WINDOW)); 412 item->SetTarget(window); 413 padM->AddItem(item); 414 padM->AddSeparatorItem(); 415 // close 416 item = new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED)); 417 item->SetTarget(window); 418 padM->AddItem(item); 419 menu->AddItem(padM); 420 // app commands 421 BMenu* appM = new BMenu("LaunchBox"); 422 appM->SetFont(be_plain_font); 423 // about 424 item = new BMenuItem("About", new BMessage(B_ABOUT_REQUESTED)); 425 item->SetTarget(be_app); 426 appM->AddItem(item); 427 // quit 428 item = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED)); 429 item->SetTarget(be_app); 430 appM->AddItem(item); 431 menu->AddItem(appM); 432 // finish popup 433 menu->SetAsyncAutoDestruct(true); 434 menu->SetFont(be_plain_font); 435 where = ConvertToScreen(where); 436 BRect mouseRect(where, where); 437 mouseRect.InsetBy(-4.0, -4.0); 438 menu->Go(where, true, false, mouseRect, true); 439 } 440 441 442 void 443 PadView::SetOrientation(enum orientation orientation) 444 { 445 if (orientation == B_VERTICAL) { 446 fButtonLayout->SetInsets(2, 7, 2, 2); 447 fButtonLayout->SetOrientation(B_VERTICAL); 448 } else { 449 fButtonLayout->SetInsets(7, 2, 2, 2); 450 fButtonLayout->SetOrientation(B_HORIZONTAL); 451 } 452 _NotifySettingsChanged(); 453 } 454 455 456 enum orientation 457 PadView::Orientation() const 458 { 459 return fButtonLayout->Orientation(); 460 } 461 462 463 void 464 PadView::SetIconSize(uint32 size) 465 { 466 if (size == fIconSize) 467 return; 468 469 fIconSize = size; 470 471 for (int32 i = 0; LaunchButton* button = ButtonAt(i); i++) 472 button->SetIconSize(fIconSize); 473 474 _NotifySettingsChanged(); 475 } 476 477 478 uint32 479 PadView::IconSize() const 480 { 481 return fIconSize; 482 } 483 484 485 void 486 PadView::SetIgnoreDoubleClick(bool refuse) 487 { 488 LaunchButton::SetIgnoreDoubleClick(refuse); 489 490 _NotifySettingsChanged(); 491 } 492 493 494 bool 495 PadView::IgnoreDoubleClick() const 496 { 497 return LaunchButton::IgnoreDoubleClick(); 498 } 499 500 501 void 502 PadView::_NotifySettingsChanged() 503 { 504 be_app->PostMessage(MSG_SETTINGS_CHANGED); 505 } 506 507