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
TaskTimer(const BMessenger & target,Task * owner)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
~TaskTimer()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
QuitRequested()57 TaskTimer::QuitRequested()
58 {
59 return true;
60 }
61
62
63 void
MessageReceived(BMessage * message)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
Start(const char * name)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
Stop(const char * name)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
_NextAlertStackCount()173 TaskTimer::_NextAlertStackCount()
174 {
175 if (sAlertStackCount > 9)
176 sAlertStackCount = 0;
177 return sAlertStackCount++;
178 }
179