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