xref: /haiku/src/apps/launchbox/PadView.cpp (revision b30304acc8c37e678a1bf66976d15bdab103f931)
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(&region);
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