xref: /haiku/src/preferences/repositories/TaskTimer.cpp (revision 856ecc7b19a4a9942cfe3010cab9bc3524870732)
15bf2b6ebSBrian Hill /*
25bf2b6ebSBrian Hill  * Copyright 2017 Haiku Inc. All rights reserved.
35bf2b6ebSBrian Hill  * Distributed under the terms of the MIT License.
45bf2b6ebSBrian Hill  *
55bf2b6ebSBrian Hill  * Authors:
65bf2b6ebSBrian Hill  *		Brian Hill
75bf2b6ebSBrian Hill  */
85bf2b6ebSBrian Hill 
95bf2b6ebSBrian Hill 
105bf2b6ebSBrian Hill #include "TaskTimer.h"
115bf2b6ebSBrian Hill 
125bf2b6ebSBrian Hill #include <Application.h>
135bf2b6ebSBrian Hill #include <Catalog.h>
145bf2b6ebSBrian Hill 
155bf2b6ebSBrian Hill #include "constants.h"
165bf2b6ebSBrian Hill 
175bf2b6ebSBrian Hill #undef B_TRANSLATION_CONTEXT
185bf2b6ebSBrian Hill #define B_TRANSLATION_CONTEXT "TaskTimer"
195bf2b6ebSBrian Hill 
205bf2b6ebSBrian Hill static int32 sAlertStackCount = 0;
215bf2b6ebSBrian Hill 
225bf2b6ebSBrian Hill 
TaskTimer(const BMessenger & target,Task * owner)235bf2b6ebSBrian Hill TaskTimer::TaskTimer(const BMessenger& target, Task* owner)
245bf2b6ebSBrian Hill 	:
255bf2b6ebSBrian Hill 	BLooper(),
265bf2b6ebSBrian Hill 	fTimeoutMicroSeconds(kTimerTimeoutSeconds * 1000000),
275bf2b6ebSBrian Hill 	fTimerIsRunning(false),
285bf2b6ebSBrian Hill 	fReplyTarget(target),
295bf2b6ebSBrian Hill 	fMessageRunner(NULL),
305bf2b6ebSBrian Hill 	fTimeoutMessage(TASK_TIMEOUT),
315bf2b6ebSBrian Hill 	fTimeoutAlert(NULL),
325bf2b6ebSBrian Hill 	fOwner(owner)
335bf2b6ebSBrian Hill {
345bf2b6ebSBrian Hill 	Run();
355bf2b6ebSBrian Hill 
365bf2b6ebSBrian Hill 	// Messenger for the Message Runner to use to send its message to the timer
375bf2b6ebSBrian Hill 	fMessenger.SetTo(this);
385bf2b6ebSBrian Hill 	// Invoker for the Alerts to use to send their messages to the timer
395bf2b6ebSBrian Hill 	fTimeoutAlertInvoker.SetMessage(
405bf2b6ebSBrian Hill 		new BMessage(TIMEOUT_ALERT_BUTTON_SELECTION));
415bf2b6ebSBrian Hill 	fTimeoutAlertInvoker.SetTarget(this);
425bf2b6ebSBrian Hill }
435bf2b6ebSBrian Hill 
445bf2b6ebSBrian Hill 
~TaskTimer()455bf2b6ebSBrian Hill TaskTimer::~TaskTimer()
465bf2b6ebSBrian Hill {
475bf2b6ebSBrian Hill 	if (fTimeoutAlert) {
485bf2b6ebSBrian Hill 		fTimeoutAlert->Lock();
495bf2b6ebSBrian Hill 		fTimeoutAlert->Quit();
505bf2b6ebSBrian Hill 	}
515bf2b6ebSBrian Hill 	if (fMessageRunner)
525bf2b6ebSBrian Hill 		fMessageRunner->SetCount(0);
535bf2b6ebSBrian Hill }
545bf2b6ebSBrian Hill 
555bf2b6ebSBrian Hill 
565bf2b6ebSBrian Hill bool
QuitRequested()575bf2b6ebSBrian Hill TaskTimer::QuitRequested()
585bf2b6ebSBrian Hill {
595bf2b6ebSBrian Hill 	return true;
605bf2b6ebSBrian Hill }
615bf2b6ebSBrian Hill 
625bf2b6ebSBrian Hill 
635bf2b6ebSBrian Hill void
MessageReceived(BMessage * message)645bf2b6ebSBrian Hill TaskTimer::MessageReceived(BMessage* message)
655bf2b6ebSBrian Hill {
665bf2b6ebSBrian Hill 	switch (message->what)
675bf2b6ebSBrian Hill 	{
68*856ecc7bSBrian Hill 		case TASK_TIMEOUT:
69*856ecc7bSBrian Hill 		{
705bf2b6ebSBrian Hill 			fMessageRunner = NULL;
715bf2b6ebSBrian Hill 			if (fTimerIsRunning) {
725bf2b6ebSBrian Hill 				BString text(B_TRANSLATE_COMMENT("The task for repository"
735bf2b6ebSBrian Hill 					" %name% is taking a long time to complete.",
745bf2b6ebSBrian Hill 					"Alert message.  Do not translate %name%"));
755bf2b6ebSBrian Hill 				BString nameString("\"");
765bf2b6ebSBrian Hill 				nameString.Append(fRepositoryName).Append("\"");
775bf2b6ebSBrian Hill 				text.ReplaceFirst("%name%", nameString);
785bf2b6ebSBrian Hill 				fTimeoutAlert = new BAlert("timeout", text,
795bf2b6ebSBrian Hill 					B_TRANSLATE_COMMENT("Keep trying", "Button label"),
805bf2b6ebSBrian Hill 					B_TRANSLATE_COMMENT("Cancel task", "Button label"),
815bf2b6ebSBrian Hill 					NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
825bf2b6ebSBrian Hill 				fTimeoutAlert->SetShortcut(0, B_ESCAPE);
835bf2b6ebSBrian Hill 				// Calculate the position to correctly stack this alert
845bf2b6ebSBrian Hill 				BRect windowFrame = be_app->WindowAt(0)->Frame();
855bf2b6ebSBrian Hill 				int32 stackPos = _NextAlertStackCount();
865bf2b6ebSBrian Hill 				float xPos = windowFrame.left
875bf2b6ebSBrian Hill 					+ windowFrame.Width()/2 + stackPos * kTimerAlertOffset;
885bf2b6ebSBrian Hill 				float yPos = windowFrame.top
895bf2b6ebSBrian Hill 					+ (stackPos + 1) * kTimerAlertOffset;
905bf2b6ebSBrian Hill 				fTimeoutAlert->Go(&fTimeoutAlertInvoker);
915bf2b6ebSBrian Hill 				xPos -= fTimeoutAlert->Frame().Width()/2;
925bf2b6ebSBrian Hill 					// The correct frame for the alert is not available until
935bf2b6ebSBrian Hill 					// after Go is called
945bf2b6ebSBrian Hill 				fTimeoutAlert->MoveTo(xPos, yPos);
955bf2b6ebSBrian Hill 			}
965bf2b6ebSBrian Hill 			break;
975bf2b6ebSBrian Hill 		}
98*856ecc7bSBrian Hill 
99*856ecc7bSBrian Hill 		case TIMEOUT_ALERT_BUTTON_SELECTION:
100*856ecc7bSBrian Hill 		{
1015bf2b6ebSBrian Hill 			fTimeoutAlert = NULL;
1025bf2b6ebSBrian Hill 			// Timeout alert was invoked by user and timer still has not
1035bf2b6ebSBrian Hill 			// been stopped
1045bf2b6ebSBrian Hill 			if (fTimerIsRunning) {
1055bf2b6ebSBrian Hill 				// find which button was pressed
1065bf2b6ebSBrian Hill 				int32 selection = -1;
1075bf2b6ebSBrian Hill 				message->FindInt32("which", &selection);
1085bf2b6ebSBrian Hill 				if (selection == 1) {
1095bf2b6ebSBrian Hill 					BMessage reply(TASK_KILL_REQUEST);
1105bf2b6ebSBrian Hill 					reply.AddPointer(key_taskptr, fOwner);
1115bf2b6ebSBrian Hill 					fReplyTarget.SendMessage(&reply);
1125bf2b6ebSBrian Hill 				} else if (selection == 0) {
1135bf2b6ebSBrian Hill 					// Create new timer
1145bf2b6ebSBrian Hill 					fMessageRunner = new BMessageRunner(fMessenger,
1155bf2b6ebSBrian Hill 						&fTimeoutMessage, kTimerRetrySeconds * 1000000, 1);
1165bf2b6ebSBrian Hill 				}
1175bf2b6ebSBrian Hill 			}
1185bf2b6ebSBrian Hill 			break;
1195bf2b6ebSBrian Hill 		}
1205bf2b6ebSBrian Hill 	}
1215bf2b6ebSBrian Hill }
1225bf2b6ebSBrian Hill 
1235bf2b6ebSBrian Hill 
1245bf2b6ebSBrian Hill void
Start(const char * name)1255bf2b6ebSBrian Hill TaskTimer::Start(const char* name)
1265bf2b6ebSBrian Hill {
1275bf2b6ebSBrian Hill 	fTimerIsRunning = true;
1285bf2b6ebSBrian Hill 	fRepositoryName.SetTo(name);
1295bf2b6ebSBrian Hill 
1305bf2b6ebSBrian Hill 	// Create a message runner that will send a TASK_TIMEOUT message if the
1315bf2b6ebSBrian Hill 	// timer is not stopped
132*856ecc7bSBrian Hill 	if (fMessageRunner == NULL) {
1335bf2b6ebSBrian Hill 		fMessageRunner = new BMessageRunner(fMessenger, &fTimeoutMessage,
1345bf2b6ebSBrian Hill 			fTimeoutMicroSeconds, 1);
135*856ecc7bSBrian Hill 	}
1365bf2b6ebSBrian Hill 	else
1375bf2b6ebSBrian Hill 		fMessageRunner->SetInterval(fTimeoutMicroSeconds);
1385bf2b6ebSBrian Hill }
1395bf2b6ebSBrian Hill 
1405bf2b6ebSBrian Hill 
1415bf2b6ebSBrian Hill void
Stop(const char * name)1425bf2b6ebSBrian Hill TaskTimer::Stop(const char* name)
1435bf2b6ebSBrian Hill {
1445bf2b6ebSBrian Hill 	fTimerIsRunning = false;
1455bf2b6ebSBrian Hill 
1465bf2b6ebSBrian Hill 	// Reset max timeout so we can reuse the runner at the next Start call
1475bf2b6ebSBrian Hill 	if (fMessageRunner != NULL)
1485bf2b6ebSBrian Hill 		fMessageRunner->SetInterval(LLONG_MAX);
1495bf2b6ebSBrian Hill 
1505bf2b6ebSBrian Hill 	// If timeout alert is showing replace it
1515bf2b6ebSBrian Hill 	if (fTimeoutAlert) {
1525bf2b6ebSBrian Hill 		// Remove current alert
1535bf2b6ebSBrian Hill 		BRect frame = fTimeoutAlert->Frame();
1545bf2b6ebSBrian Hill 		fTimeoutAlert->Quit();
1555bf2b6ebSBrian Hill 		fTimeoutAlert = NULL;
1565bf2b6ebSBrian Hill 
1575bf2b6ebSBrian Hill 		// Display new alert that won't send a message
1585bf2b6ebSBrian Hill 		BString text(B_TRANSLATE_COMMENT("Good news! The task for repository "
1595bf2b6ebSBrian Hill 			"%name% completed.", "Alert message.  Do not translate %name%"));
1605bf2b6ebSBrian Hill 		BString nameString("\"");
1615bf2b6ebSBrian Hill 		nameString.Append(name).Append("\"");
1625bf2b6ebSBrian Hill 		text.ReplaceFirst("%name%", nameString);
1635bf2b6ebSBrian Hill 		BAlert* newAlert = new BAlert("timeout", text, kOKLabel, NULL, NULL,
1645bf2b6ebSBrian Hill 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1655bf2b6ebSBrian Hill 		newAlert->SetShortcut(0, B_ESCAPE);
1665bf2b6ebSBrian Hill 		newAlert->MoveTo(frame.left, frame.top);
1675bf2b6ebSBrian Hill 		newAlert->Go(NULL);
1685bf2b6ebSBrian Hill 	}
1695bf2b6ebSBrian Hill }
1705bf2b6ebSBrian Hill 
1715bf2b6ebSBrian Hill 
1725bf2b6ebSBrian Hill int32
_NextAlertStackCount()1735bf2b6ebSBrian Hill TaskTimer::_NextAlertStackCount()
1745bf2b6ebSBrian Hill {
1755bf2b6ebSBrian Hill 	if (sAlertStackCount > 9)
1765bf2b6ebSBrian Hill 		sAlertStackCount = 0;
1775bf2b6ebSBrian Hill 	return sAlertStackCount++;
1785bf2b6ebSBrian Hill }
179