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