1 /* 2 * Copyright 2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "PadView.h" 10 11 #include <stdio.h> 12 13 #include <Application.h> 14 #include <GroupLayout.h> 15 #include <MenuItem.h> 16 #include <Message.h> 17 #include <PopUpMenu.h> 18 #include <Region.h> 19 #include <Screen.h> 20 #include <SpaceLayoutItem.h> 21 22 #include "LaunchButton.h" 23 #include "MainWindow.h" 24 25 bigtime_t kActivationDelay = 40000; 26 27 enum { 28 MSG_TOGGLE_LAYOUT = 'tgll' 29 }; 30 31 // constructor 32 PadView::PadView(const char* name) 33 : BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, NULL), 34 fDragging(false), 35 fClickTime(0), 36 fButtonLayout(new BGroupLayout(B_VERTICAL, 4)) 37 { 38 SetViewColor(B_TRANSPARENT_32_BIT); 39 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 40 get_click_speed(&kActivationDelay); 41 42 fButtonLayout->SetInsets(2, 7, 2, 2); 43 SetLayout(fButtonLayout); 44 } 45 46 // destructor 47 PadView::~PadView() 48 { 49 } 50 51 // Draw 52 void 53 PadView::Draw(BRect updateRect) 54 { 55 rgb_color background = LowColor(); 56 rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT); 57 rgb_color shadow = tint_color(background, B_DARKEN_2_TINT); 58 BRect r(Bounds()); 59 BeginLineArray(4); 60 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), light); 61 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), light); 62 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), shadow); 63 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), shadow); 64 EndLineArray(); 65 r.InsetBy(1.0, 1.0); 66 StrokeRect(r, B_SOLID_LOW); 67 r.InsetBy(1.0, 1.0); 68 // dots along top 69 BPoint dot = r.LeftTop(); 70 int32 current; 71 int32 stop; 72 BPoint offset; 73 BPoint next; 74 if (Orientation() == B_VERTICAL) { 75 current = (int32)dot.x; 76 stop = (int32)r.right; 77 offset = BPoint(0, 1); 78 next = BPoint(1, -4); 79 r.top += 5.0; 80 } else { 81 current = (int32)dot.y; 82 stop = (int32)r.bottom; 83 offset = BPoint(1, 0); 84 next = BPoint(-4, 1); 85 r.left += 5.0; 86 } 87 int32 num = 1; 88 while (current <= stop) { 89 rgb_color col1; 90 rgb_color col2; 91 switch (num) { 92 case 1: 93 col1 = shadow; 94 col2 = background; 95 break; 96 case 2: 97 col1 = background; 98 col2 = light; 99 break; 100 case 3: 101 col1 = background; 102 col2 = background; 103 num = 0; 104 break; 105 } 106 SetHighColor(col1); 107 StrokeLine(dot, dot, B_SOLID_HIGH); 108 SetHighColor(col2); 109 dot += offset; 110 StrokeLine(dot, dot, B_SOLID_HIGH); 111 dot += offset; 112 StrokeLine(dot, dot, B_SOLID_LOW); 113 dot += offset; 114 SetHighColor(col1); 115 StrokeLine(dot, dot, B_SOLID_HIGH); 116 dot += offset; 117 SetHighColor(col2); 118 StrokeLine(dot, dot, B_SOLID_HIGH); 119 // next pixel 120 num++; 121 dot += next; 122 current++; 123 } 124 FillRect(r, B_SOLID_LOW); 125 } 126 127 // MessageReceived 128 void 129 PadView::MessageReceived(BMessage* message) 130 { 131 switch (message->what) { 132 case MSG_TOGGLE_LAYOUT: 133 if (fButtonLayout->Orientation() == B_HORIZONTAL) { 134 fButtonLayout->SetInsets(2, 7, 2, 2); 135 fButtonLayout->SetOrientation(B_VERTICAL); 136 } else { 137 fButtonLayout->SetInsets(7, 2, 2, 2); 138 fButtonLayout->SetOrientation(B_HORIZONTAL); 139 } 140 break; 141 default: 142 BView::MessageReceived(message); 143 break; 144 } 145 } 146 147 // MouseDown 148 void 149 PadView::MouseDown(BPoint where) 150 { 151 if (BWindow* window = Window()) { 152 BRegion region; 153 GetClippingRegion(®ion); 154 if (region.Contains(where)) { 155 bool handle = true; 156 for (int32 i = 0; BView* child = ChildAt(i); i++) { 157 if (child->Frame().Contains(where)) { 158 handle = false; 159 break; 160 } 161 } 162 if (handle) { 163 if (BMessage* message = window->CurrentMessage()) { 164 uint32 buttons; 165 message->FindInt32("buttons", (int32*)&buttons); 166 if (buttons & B_SECONDARY_MOUSE_BUTTON) { 167 BRect r = Bounds(); 168 r.InsetBy(2.0, 2.0); 169 r.top += 6.0; 170 if (r.Contains(where)) { 171 DisplayMenu(where); 172 } else { 173 // sends the window to the back 174 window->Activate(false); 175 } 176 } else { 177 if (system_time() - fClickTime < kActivationDelay) { 178 window->Minimize(true); 179 fClickTime = 0; 180 } else { 181 window->Activate(); 182 fDragOffset = ConvertToScreen(where) - window->Frame().LeftTop(); 183 fDragging = true; 184 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 185 fClickTime = system_time(); 186 } 187 } 188 } 189 } 190 } 191 } 192 } 193 194 // MouseUp 195 void 196 PadView::MouseUp(BPoint where) 197 { 198 if (BWindow* window = Window()) { 199 uint32 buttons; 200 window->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 201 if (buttons & B_PRIMARY_MOUSE_BUTTON 202 && system_time() - fClickTime < kActivationDelay 203 && window->IsActive()) 204 window->Activate(); 205 } 206 fDragging = false; 207 } 208 209 // MouseMoved 210 void 211 PadView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 212 { 213 if (MainWindow* window = dynamic_cast<MainWindow*>(Window())) { 214 if (fDragging) { 215 window->MoveTo(ConvertToScreen(where) - fDragOffset); 216 } else if (window->AutoRaise()) { 217 where = ConvertToScreen(where); 218 BScreen screen(window); 219 BRect frame = screen.Frame(); 220 BRect windowFrame = window->Frame(); 221 if (where.x == frame.left || where.x == frame.right 222 || where.y == frame.top || where.y == frame.bottom) { 223 BPoint position = window->ScreenPosition(); 224 bool raise = false; 225 if (fabs(0.5 - position.x) > fabs(0.5 - position.y)) { 226 // left or right border 227 if (where.y >= windowFrame.top && where.y <= windowFrame.bottom) { 228 if (position.x < 0.5 && where.x == frame.left) 229 raise = true; 230 else if (position.x > 0.5 && where.x == frame.right) 231 raise = true; 232 } 233 } else { 234 // top or bottom border 235 if (where.x >= windowFrame.left && where.x <= windowFrame.right) { 236 if (position.y < 0.5 && where.y == frame.top) 237 raise = true; 238 else if (position.y > 0.5 && where.y == frame.top) 239 raise = true; 240 } 241 } 242 if (raise) 243 window->Activate(); 244 } 245 } 246 } 247 } 248 249 // AddButton 250 void 251 PadView::AddButton(LaunchButton* button, LaunchButton* beforeButton) 252 { 253 if (beforeButton) 254 fButtonLayout->AddView(fButtonLayout->IndexOfView(beforeButton), button); 255 else 256 fButtonLayout->AddView(button); 257 } 258 259 // RemoveButton 260 bool 261 PadView::RemoveButton(LaunchButton* button) 262 { 263 return fButtonLayout->RemoveView(button); 264 } 265 266 // ButtonAt 267 LaunchButton* 268 PadView::ButtonAt(int32 index) const 269 { 270 return dynamic_cast<LaunchButton*>(ChildAt(index)); 271 } 272 273 // DisplayMenu 274 void 275 PadView::DisplayMenu(BPoint where, LaunchButton* button) const 276 { 277 if (MainWindow* window = dynamic_cast<MainWindow*>(Window())) { 278 LaunchButton* nearestButton = button; 279 if (!nearestButton) { 280 // find the nearest button 281 for (int32 i = 0; (nearestButton = ButtonAt(i)); i++) { 282 if (nearestButton->Frame().top > where.y) 283 break; 284 } 285 } 286 BPopUpMenu* menu = new BPopUpMenu("launch popup", false, false); 287 // add button 288 BMessage* message = new BMessage(MSG_ADD_SLOT); 289 message->AddPointer("be:source", (void*)nearestButton); 290 BMenuItem* item = new BMenuItem("Add Button Here", message); 291 item->SetTarget(window); 292 menu->AddItem(item); 293 // button options 294 if (button) { 295 // remove button 296 message = new BMessage(MSG_CLEAR_SLOT); 297 message->AddPointer("be:source", (void*)button); 298 item = new BMenuItem("Clear Button", message); 299 item->SetTarget(window); 300 menu->AddItem(item); 301 // remove button 302 message = new BMessage(MSG_REMOVE_SLOT); 303 message->AddPointer("be:source", (void*)button); 304 item = new BMenuItem("Remove Button", message); 305 item->SetTarget(window); 306 menu->AddItem(item); 307 // TODO: disabled because Haiku does not yet support tool tips 308 // if (button->Ref()) { 309 // message = new BMessage(MSG_SET_DESCRIPTION); 310 // message->AddPointer("be:source", (void*)button); 311 // item = new BMenuItem("Set Description"B_UTF8_ELLIPSIS, message); 312 // item->SetTarget(window); 313 // menu->AddItem(item); 314 // } 315 } 316 menu->AddSeparatorItem(); 317 // window settings 318 BMenu* settingsM = new BMenu("Settings"); 319 settingsM->SetFont(be_plain_font); 320 321 const char* toggleLayoutLabel; 322 if (fButtonLayout->Orientation() == B_HORIZONTAL) 323 toggleLayoutLabel = "Vertical Layout"; 324 else 325 toggleLayoutLabel = "Horizontal Layout"; 326 item = new BMenuItem(toggleLayoutLabel, new BMessage(MSG_TOGGLE_LAYOUT)); 327 item->SetTarget(this); 328 settingsM->AddItem(item); 329 330 uint32 what = window->Look() == B_BORDERED_WINDOW_LOOK ? MSG_SHOW_BORDER : MSG_HIDE_BORDER; 331 item = new BMenuItem("Show Window Border", new BMessage(what)); 332 item->SetTarget(window); 333 item->SetMarked(what == MSG_HIDE_BORDER); 334 settingsM->AddItem(item); 335 336 item = new BMenuItem("Auto Raise", new BMessage(MSG_TOGGLE_AUTORAISE)); 337 item->SetTarget(window); 338 item->SetMarked(window->AutoRaise()); 339 settingsM->AddItem(item); 340 341 item = new BMenuItem("Show On All Workspaces", new BMessage(MSG_SHOW_ON_ALL_WORKSPACES)); 342 item->SetTarget(window); 343 item->SetMarked(window->ShowOnAllWorkspaces()); 344 settingsM->AddItem(item); 345 346 menu->AddItem(settingsM); 347 348 menu->AddSeparatorItem(); 349 350 // pad commands 351 BMenu* padM = new BMenu("Pad"); 352 padM->SetFont(be_plain_font); 353 // new pad 354 item = new BMenuItem("New", new BMessage(MSG_ADD_WINDOW)); 355 item->SetTarget(be_app); 356 padM->AddItem(item); 357 // new pad 358 item = new BMenuItem("Clone", new BMessage(MSG_ADD_WINDOW)); 359 item->SetTarget(window); 360 padM->AddItem(item); 361 padM->AddSeparatorItem(); 362 // close 363 item = new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED)); 364 item->SetTarget(window); 365 padM->AddItem(item); 366 menu->AddItem(padM); 367 // app commands 368 BMenu* appM = new BMenu("LaunchBox"); 369 appM->SetFont(be_plain_font); 370 // about 371 item = new BMenuItem("About", new BMessage(B_ABOUT_REQUESTED)); 372 item->SetTarget(be_app); 373 appM->AddItem(item); 374 // quit 375 item = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED)); 376 item->SetTarget(be_app); 377 appM->AddItem(item); 378 menu->AddItem(appM); 379 // finish popup 380 menu->SetAsyncAutoDestruct(true); 381 menu->SetFont(be_plain_font); 382 where = ConvertToScreen(where); 383 BRect mouseRect(where, where); 384 mouseRect.InsetBy(-4.0, -4.0); 385 menu->Go(where, true, false, mouseRect, true); 386 } 387 } 388 389 // SetOrientation 390 void 391 PadView::SetOrientation(enum orientation orientation) 392 { 393 if (orientation == B_VERTICAL) { 394 fButtonLayout->SetInsets(2, 7, 2, 2); 395 fButtonLayout->SetOrientation(B_VERTICAL); 396 } else { 397 fButtonLayout->SetInsets(7, 2, 2, 2); 398 fButtonLayout->SetOrientation(B_HORIZONTAL); 399 } 400 } 401 402 // Orientation 403 enum orientation 404 PadView::Orientation() const 405 { 406 return fButtonLayout->Orientation(); 407 } 408 409 410