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