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