xref: /haiku/src/preferences/repositories/TaskTimer.cpp (revision 5bf2b6eb74cbbee276a8050078917e8fbbea11e9)
1*5bf2b6ebSBrian Hill /*
2*5bf2b6ebSBrian Hill  * Copyright 2017 Haiku Inc. All rights reserved.
3*5bf2b6ebSBrian Hill  * Distributed under the terms of the MIT License.
4*5bf2b6ebSBrian Hill  *
5*5bf2b6ebSBrian Hill  * Authors:
6*5bf2b6ebSBrian Hill  *		Brian Hill
7*5bf2b6ebSBrian Hill  */
8*5bf2b6ebSBrian Hill 
9*5bf2b6ebSBrian Hill 
10*5bf2b6ebSBrian Hill #include "TaskTimer.h"
11*5bf2b6ebSBrian Hill 
12*5bf2b6ebSBrian Hill #include <Application.h>
13*5bf2b6ebSBrian Hill #include <Catalog.h>
14*5bf2b6ebSBrian Hill 
15*5bf2b6ebSBrian Hill #include "constants.h"
16*5bf2b6ebSBrian Hill 
17*5bf2b6ebSBrian Hill #undef B_TRANSLATION_CONTEXT
18*5bf2b6ebSBrian Hill #define B_TRANSLATION_CONTEXT "TaskTimer"
19*5bf2b6ebSBrian Hill 
20*5bf2b6ebSBrian Hill static int32 sAlertStackCount = 0;
21*5bf2b6ebSBrian Hill 
22*5bf2b6ebSBrian Hill 
23*5bf2b6ebSBrian Hill TaskTimer::TaskTimer(const BMessenger& target, Task* owner)
24*5bf2b6ebSBrian Hill 	:
25*5bf2b6ebSBrian Hill 	BLooper(),
26*5bf2b6ebSBrian Hill 	fTimeoutMicroSeconds(kTimerTimeoutSeconds * 1000000),
27*5bf2b6ebSBrian Hill 	fTimerIsRunning(false),
28*5bf2b6ebSBrian Hill 	fReplyTarget(target),
29*5bf2b6ebSBrian Hill 	fMessageRunner(NULL),
30*5bf2b6ebSBrian Hill 	fTimeoutMessage(TASK_TIMEOUT),
31*5bf2b6ebSBrian Hill 	fTimeoutAlert(NULL),
32*5bf2b6ebSBrian Hill 	fOwner(owner)
33*5bf2b6ebSBrian Hill {
34*5bf2b6ebSBrian Hill 	Run();
35*5bf2b6ebSBrian Hill 
36*5bf2b6ebSBrian Hill 	// Messenger for the Message Runner to use to send its message to the timer
37*5bf2b6ebSBrian Hill 	fMessenger.SetTo(this);
38*5bf2b6ebSBrian Hill 	// Invoker for the Alerts to use to send their messages to the timer
39*5bf2b6ebSBrian Hill 	fTimeoutAlertInvoker.SetMessage(
40*5bf2b6ebSBrian Hill 		new BMessage(TIMEOUT_ALERT_BUTTON_SELECTION));
41*5bf2b6ebSBrian Hill 	fTimeoutAlertInvoker.SetTarget(this);
42*5bf2b6ebSBrian Hill }
43*5bf2b6ebSBrian Hill 
44*5bf2b6ebSBrian Hill 
45*5bf2b6ebSBrian Hill TaskTimer::~TaskTimer()
46*5bf2b6ebSBrian Hill {
47*5bf2b6ebSBrian Hill 	if (fTimeoutAlert) {
48*5bf2b6ebSBrian Hill 		fTimeoutAlert->Lock();
49*5bf2b6ebSBrian Hill 		fTimeoutAlert->Quit();
50*5bf2b6ebSBrian Hill 	}
51*5bf2b6ebSBrian Hill 	if (fMessageRunner)
52*5bf2b6ebSBrian Hill 		fMessageRunner->SetCount(0);
53*5bf2b6ebSBrian Hill }
54*5bf2b6ebSBrian Hill 
55*5bf2b6ebSBrian Hill 
56*5bf2b6ebSBrian Hill bool
57*5bf2b6ebSBrian Hill TaskTimer::QuitRequested()
58*5bf2b6ebSBrian Hill {
59*5bf2b6ebSBrian Hill 	return true;
60*5bf2b6ebSBrian Hill }
61*5bf2b6ebSBrian Hill 
62*5bf2b6ebSBrian Hill 
63*5bf2b6ebSBrian Hill void
64*5bf2b6ebSBrian Hill TaskTimer::MessageReceived(BMessage* message)
65*5bf2b6ebSBrian Hill {
66*5bf2b6ebSBrian Hill 	switch (message->what)
67*5bf2b6ebSBrian Hill 	{
68*5bf2b6ebSBrian Hill 		case TASK_TIMEOUT: {
69*5bf2b6ebSBrian Hill 			fMessageRunner = NULL;
70*5bf2b6ebSBrian Hill 			if (fTimerIsRunning) {
71*5bf2b6ebSBrian Hill 				BString text(B_TRANSLATE_COMMENT("The task for repository"
72*5bf2b6ebSBrian Hill 					" %name% is taking a long time to complete.",
73*5bf2b6ebSBrian Hill 					"Alert message.  Do not translate %name%"));
74*5bf2b6ebSBrian Hill 				BString nameString("\"");
75*5bf2b6ebSBrian Hill 				nameString.Append(fRepositoryName).Append("\"");
76*5bf2b6ebSBrian Hill 				text.ReplaceFirst("%name%", nameString);
77*5bf2b6ebSBrian Hill 				fTimeoutAlert = new BAlert("timeout", text,
78*5bf2b6ebSBrian Hill 					B_TRANSLATE_COMMENT("Keep trying", "Button label"),
79*5bf2b6ebSBrian Hill 					B_TRANSLATE_COMMENT("Cancel task", "Button label"),
80*5bf2b6ebSBrian Hill 					NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
81*5bf2b6ebSBrian Hill 				fTimeoutAlert->SetShortcut(0, B_ESCAPE);
82*5bf2b6ebSBrian Hill 				// Calculate the position to correctly stack this alert
83*5bf2b6ebSBrian Hill 				BRect windowFrame = be_app->WindowAt(0)->Frame();
84*5bf2b6ebSBrian Hill 				int32 stackPos = _NextAlertStackCount();
85*5bf2b6ebSBrian Hill 				float xPos = windowFrame.left
86*5bf2b6ebSBrian Hill 					+ windowFrame.Width()/2 + stackPos * kTimerAlertOffset;
87*5bf2b6ebSBrian Hill 				float yPos = windowFrame.top
88*5bf2b6ebSBrian Hill 					+ (stackPos + 1) * kTimerAlertOffset;
89*5bf2b6ebSBrian Hill 				fTimeoutAlert->Go(&fTimeoutAlertInvoker);
90*5bf2b6ebSBrian Hill 				xPos -= fTimeoutAlert->Frame().Width()/2;
91*5bf2b6ebSBrian Hill 					// The correct frame for the alert is not available until
92*5bf2b6ebSBrian Hill 					// after Go is called
93*5bf2b6ebSBrian Hill 				fTimeoutAlert->MoveTo(xPos, yPos);
94*5bf2b6ebSBrian Hill 			}
95*5bf2b6ebSBrian Hill 			break;
96*5bf2b6ebSBrian Hill 		}
97*5bf2b6ebSBrian Hill 		case TIMEOUT_ALERT_BUTTON_SELECTION: {
98*5bf2b6ebSBrian Hill 			fTimeoutAlert = NULL;
99*5bf2b6ebSBrian Hill 			// Timeout alert was invoked by user and timer still has not
100*5bf2b6ebSBrian Hill 			// been stopped
101*5bf2b6ebSBrian Hill 			if (fTimerIsRunning) {
102*5bf2b6ebSBrian Hill 				// find which button was pressed
103*5bf2b6ebSBrian Hill 				int32 selection = -1;
104*5bf2b6ebSBrian Hill 				message->FindInt32("which", &selection);
105*5bf2b6ebSBrian Hill 				if (selection == 1) {
106*5bf2b6ebSBrian Hill 					BMessage reply(TASK_KILL_REQUEST);
107*5bf2b6ebSBrian Hill 					reply.AddPointer(key_taskptr, fOwner);
108*5bf2b6ebSBrian Hill 					fReplyTarget.SendMessage(&reply);
109*5bf2b6ebSBrian Hill 				} else if (selection == 0) {
110*5bf2b6ebSBrian Hill 					// Create new timer
111*5bf2b6ebSBrian Hill 					fMessageRunner = new BMessageRunner(fMessenger,
112*5bf2b6ebSBrian Hill 						&fTimeoutMessage, kTimerRetrySeconds * 1000000, 1);
113*5bf2b6ebSBrian Hill 				}
114*5bf2b6ebSBrian Hill 			}
115*5bf2b6ebSBrian Hill 			break;
116*5bf2b6ebSBrian Hill 		}
117*5bf2b6ebSBrian Hill 	}
118*5bf2b6ebSBrian Hill }
119*5bf2b6ebSBrian Hill 
120*5bf2b6ebSBrian Hill 
121*5bf2b6ebSBrian Hill void
122*5bf2b6ebSBrian Hill TaskTimer::Start(const char* name)
123*5bf2b6ebSBrian Hill {
124*5bf2b6ebSBrian Hill 	fTimerIsRunning = true;
125*5bf2b6ebSBrian Hill 	fRepositoryName.SetTo(name);
126*5bf2b6ebSBrian Hill 
127*5bf2b6ebSBrian Hill 	// Create a message runner that will send a TASK_TIMEOUT message if the
128*5bf2b6ebSBrian Hill 	// timer is not stopped
129*5bf2b6ebSBrian Hill 	if (fMessageRunner == NULL)
130*5bf2b6ebSBrian Hill 		fMessageRunner = new BMessageRunner(fMessenger, &fTimeoutMessage,
131*5bf2b6ebSBrian Hill 			fTimeoutMicroSeconds, 1);
132*5bf2b6ebSBrian Hill 	else
133*5bf2b6ebSBrian Hill 		fMessageRunner->SetInterval(fTimeoutMicroSeconds);
134*5bf2b6ebSBrian Hill }
135*5bf2b6ebSBrian Hill 
136*5bf2b6ebSBrian Hill 
137*5bf2b6ebSBrian Hill void
138*5bf2b6ebSBrian Hill TaskTimer::Stop(const char* name)
139*5bf2b6ebSBrian Hill {
140*5bf2b6ebSBrian Hill 	fTimerIsRunning = false;
141*5bf2b6ebSBrian Hill 
142*5bf2b6ebSBrian Hill 	// Reset max timeout so we can reuse the runner at the next Start call
143*5bf2b6ebSBrian Hill 	if (fMessageRunner != NULL)
144*5bf2b6ebSBrian Hill 		fMessageRunner->SetInterval(LLONG_MAX);
145*5bf2b6ebSBrian Hill 
146*5bf2b6ebSBrian Hill 	// If timeout alert is showing replace it
147*5bf2b6ebSBrian Hill 	if (fTimeoutAlert) {
148*5bf2b6ebSBrian Hill 		// Remove current alert
149*5bf2b6ebSBrian Hill 		BRect frame = fTimeoutAlert->Frame();
150*5bf2b6ebSBrian Hill 		fTimeoutAlert->Quit();
151*5bf2b6ebSBrian Hill 		fTimeoutAlert = NULL;
152*5bf2b6ebSBrian Hill 
153*5bf2b6ebSBrian Hill 		// Display new alert that won't send a message
154*5bf2b6ebSBrian Hill 		BString text(B_TRANSLATE_COMMENT("Good news! The task for repository "
155*5bf2b6ebSBrian Hill 			"%name% completed.", "Alert message.  Do not translate %name%"));
156*5bf2b6ebSBrian Hill 		BString nameString("\"");
157*5bf2b6ebSBrian Hill 		nameString.Append(name).Append("\"");
158*5bf2b6ebSBrian Hill 		text.ReplaceFirst("%name%", nameString);
159*5bf2b6ebSBrian Hill 		BAlert* newAlert = new BAlert("timeout", text, kOKLabel, NULL, NULL,
160*5bf2b6ebSBrian Hill 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
161*5bf2b6ebSBrian Hill 		newAlert->SetShortcut(0, B_ESCAPE);
162*5bf2b6ebSBrian Hill 		newAlert->MoveTo(frame.left, frame.top);
163*5bf2b6ebSBrian Hill 		newAlert->Go(NULL);
164*5bf2b6ebSBrian Hill 	}
165*5bf2b6ebSBrian Hill }
166*5bf2b6ebSBrian Hill 
167*5bf2b6ebSBrian Hill 
168*5bf2b6ebSBrian Hill int32
169*5bf2b6ebSBrian Hill TaskTimer::_NextAlertStackCount()
170*5bf2b6ebSBrian Hill {
171*5bf2b6ebSBrian Hill 	if (sAlertStackCount > 9)
172*5bf2b6ebSBrian Hill 		sAlertStackCount = 0;
173*5bf2b6ebSBrian Hill 	return sAlertStackCount++;
174*5bf2b6ebSBrian Hill }
175