1/* 2 * Copyright 2003-2009 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * J��r��me Duval, 7 * Axel D��rfler (axeld@pinc-software.de) 8 * Andrew McCall (mccall@digitalparadise.co.uk) 9 * Philippe Saint-Pierre stpere@gmail.com 10 */ 11 12 13#include "MouseView.h" 14 15#include <Box.h> 16#include <Button.h> 17#include <Debug.h> 18#include <GradientLinear.h> 19#include <MenuField.h> 20#include <MenuItem.h> 21#include <PopUpMenu.h> 22#include <Region.h> 23#include <Slider.h> 24#include <TextControl.h> 25#include <TranslationUtils.h> 26#include <TranslatorFormats.h> 27#include <Window.h> 28 29#include "MouseConstants.h" 30#include "MouseSettings.h" 31#include "MouseWindow.h" 32 33static const int32 kButtonTop = 6; 34static const int32 kMouseDownWidth = 72; 35static const int32 kMouseDownHeight = 30; 36 37static const int32 kOneButtonOffsets[4] 38 = { 0, kMouseDownWidth }; 39static const int32 kTwoButtonOffsets[4] 40 = { 0, kMouseDownWidth / 2, kMouseDownWidth }; 41static const int32 kThreeButtonOffsets[4] 42 = { 0, kMouseDownWidth / 3, kMouseDownWidth / 3 * 2, kMouseDownWidth }; 43 44static const rgb_color kButtonPressedColor = { 120, 120, 120 }; 45static const rgb_color kButtonReleasedLeftColor = { 255, 255, 255 }; 46static const rgb_color kButtonReleasedRightColor = { 184, 184, 184 }; 47static const rgb_color kButtonPressedSeparatorColor = { 48, 48, 48 }; 48static const rgb_color kButtonReleasedSeparatorColor = { 88, 88, 88 }; 49static const rgb_color kButtonTextColor = { 32, 32, 32 }; 50static const rgb_color kMouseShadowColor = { 150, 150, 150 }; 51static const rgb_color kMouseBodyTopColor = { 210, 210, 210 }; 52static const rgb_color kMouseBodyBottomColor = { 140, 140, 140 }; 53static const rgb_color kMouseOutlineColor = { 0, 0, 0 }; 54 55static const int32* 56getButtonOffsets(int32 type) 57{ 58 switch (type) { 59 case 1: 60 return kOneButtonOffsets; 61 case 2: 62 return kTwoButtonOffsets; 63 case 3: 64 default: 65 return kThreeButtonOffsets; 66 } 67} 68 69 70static uint32 71getMappingNumber(int32 mapping) 72{ 73 switch (mapping) { 74 case B_PRIMARY_MOUSE_BUTTON: 75 return 0; 76 case B_SECONDARY_MOUSE_BUTTON: 77 return 1; 78 case B_TERTIARY_MOUSE_BUTTON: 79 return 2; 80 } 81 return 0; 82} 83 84 85// #pragma mark - 86 87 88MouseView::MouseView(const MouseSettings &settings) 89 : 90 BView("mouse_view", B_PULSE_NEEDED | B_WILL_DRAW), 91 fSettings(settings), 92 fType(-1), 93 fButtons(0), 94 fOldButtons(0) 95{ 96 SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 97} 98 99 100MouseView::~MouseView() 101{ 102} 103 104 105void 106MouseView::GetPreferredSize(float* _width, float* _height) 107{ 108 if (_width) 109 *_width = kMouseDownWidth + 2; 110 if (_height) 111 *_height = 100; 112} 113 114 115void 116MouseView::MouseUp(BPoint) 117{ 118 fButtons = 0; 119 Invalidate(BRect(0, kButtonTop, kMouseDownWidth, 120 kButtonTop + kMouseDownHeight)); 121 fOldButtons = fButtons; 122} 123 124 125void 126MouseView::MouseDown(BPoint where) 127{ 128 BMessage *mouseMsg = Window()->CurrentMessage(); 129 fButtons = mouseMsg->FindInt32("buttons"); 130 int32 modifiers = mouseMsg->FindInt32("modifiers"); 131 if (modifiers & B_CONTROL_KEY) { 132 if (modifiers & B_COMMAND_KEY) 133 fButtons = B_TERTIARY_MOUSE_BUTTON; 134 else 135 fButtons = B_SECONDARY_MOUSE_BUTTON; 136 } 137 138 // Get the current clipping region before requesting any updates. 139 // Otherwise those parts would be excluded from the region. 140 BRegion clipping; 141 GetClippingRegion(&clipping); 142 143 if (fOldButtons != fButtons) { 144 Invalidate(BRect(0, kButtonTop, kMouseDownWidth, kButtonTop 145 + kMouseDownHeight)); 146 fOldButtons = fButtons; 147 } 148 149 const int32* offset = getButtonOffsets(fType); 150 int32 button = -1; 151 for (int32 i = 0; i <= fType; i++) { 152 BRect frame(offset[i], kButtonTop, offset[i + 1] - 1, 153 kButtonTop + kMouseDownHeight); 154 if (frame.Contains(where)) { 155 button = i; 156 break; 157 } 158 } 159 if (button < 0) 160 return; 161 162 // We are setup to receive all mouse events, even if our window 163 // is not active, so make sure that we don't display the menu when 164 // the user clicked inside our view, but another window is on top. 165 if (clipping.Contains(where)) { 166 button = _ConvertFromVisualOrder(button); 167 168 BPopUpMenu menu("Mouse Map Menu"); 169 BMessage message(kMsgMouseMap); 170 message.AddInt32("button", button); 171 172 menu.AddItem(new BMenuItem("1", new BMessage(message))); 173 menu.AddItem(new BMenuItem("2", new BMessage(message))); 174 menu.AddItem(new BMenuItem("3", new BMessage(message))); 175 176 menu.ItemAt(getMappingNumber(fSettings.Mapping(button))) 177 ->SetMarked(true); 178 menu.SetTargetForItems(Window()); 179 180 ConvertToScreen(&where); 181 menu.Go(where, true); 182 } 183} 184 185 186void 187MouseView::AttachedToWindow() 188{ 189 if (Parent() != NULL) 190 SetViewColor(Parent()->ViewColor()); 191 else 192 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 193 194 UpdateFromSettings(); 195} 196 197 198void 199MouseView::Draw(BRect updateFrame) 200{ 201 BRect mouseBody(Bounds()); 202 mouseBody.right -= 2; 203 mouseBody.bottom -= 2; 204 float radius = 10; 205 206 // Draw the shadow 207 SetHighColor(kMouseShadowColor); 208 FillRoundRect(mouseBody.OffsetByCopy(2, 2), radius, radius); 209 210 // Draw the body 211 BGradientLinear gradient(mouseBody.LeftTop(), mouseBody.LeftBottom()); 212 gradient.AddColor(kMouseBodyTopColor, 0); 213 gradient.AddColor(kMouseBodyBottomColor, 255); 214 FillRoundRect(mouseBody, radius, radius, gradient); 215 216 // Draw the outline 217 SetHighColor(kMouseOutlineColor); 218 StrokeRoundRect(mouseBody, radius, radius); 219 220 mouse_map map; 221 fSettings.Mapping(map); 222 223 const int32* offset = getButtonOffsets(fType); 224 bool middlePressed = fType == 3 && (map.button[2] & fButtons) != 0; 225 226 for (int32 i = 0; i < fType; i++) { 227 BRect border(offset[i] + 1, kButtonTop + 2, offset[i + 1] - 1, 228 kButtonTop + kMouseDownHeight - 4); 229 bool pressed = (fButtons & map.button[_ConvertFromVisualOrder(i)]) != 0; 230 // is button currently pressed? 231 232 if (pressed) { 233 BRect frame(offset[i], 0, offset[i + 1], kMouseDownHeight - 1); 234 frame.InsetBy(1, 1); 235 SetHighColor(kButtonPressedColor); 236 FillRect(frame.OffsetByCopy(0, kButtonTop)); 237 } 238 239 SetDrawingMode(B_OP_OVER); 240 241 if (i > 0 && fType > i) { 242 // left border 243 SetHighColor(pressed ? 244 kButtonPressedColor : kButtonReleasedLeftColor); 245 StrokeLine(BPoint(border.LeftTop()), BPoint(border.LeftBottom())); 246 247 // draw separator 248 BRect separator = border.OffsetByCopy(-1, -1); 249 separator.bottom += 2; 250 if (!middlePressed) 251 border.top++; 252 253 SetHighColor(middlePressed ? 254 kButtonPressedSeparatorColor : kButtonReleasedSeparatorColor); 255 StrokeLine(BPoint(separator.LeftTop()), 256 BPoint(separator.LeftBottom())); 257 } 258 259 if (fType > 1 && i + 1 < fType) { 260 // right border 261 SetHighColor(pressed ? 262 kButtonPressedColor : kButtonReleasedRightColor); 263 StrokeLine(BPoint(border.RightTop()), 264 BPoint(border.RightBottom())); 265 } 266 267 // draw mapping number centered over the button 268 269 SetHighColor(kButtonTextColor); 270 271 char number[2] = {0}; 272 number[0] = getMappingNumber(map.button[_ConvertFromVisualOrder(i)]) 273 + '1'; 274 275 DrawString(number, BPoint(border.left + 276 (border.Width() - StringWidth(number)) / 2, kButtonTop + 18)); 277 } 278} 279 280 281int32 282MouseView::_ConvertFromVisualOrder(int32 i) 283{ 284 if (fType < 3) 285 return i; 286 287 switch (i) { 288 case 0: 289 default: 290 return 0; 291 case 1: 292 return 2; 293 case 2: 294 return 1; 295 } 296} 297 298 299void 300MouseView::SetMouseType(int32 type) 301{ 302 fType = type; 303 Invalidate(); 304} 305 306 307void 308MouseView::MouseMapUpdated() 309{ 310 Invalidate(); 311} 312 313 314void 315MouseView::UpdateFromSettings() 316{ 317 SetMouseType(fSettings.MouseType()); 318} 319 320