home *** CD-ROM | disk | FTP | other *** search
- /*
- * Task Priority Manager
- * Copyright 1993, 1994 Barry McConnell
- * bmccnnll@tcd.ie
- *
- * Handle events from the windows.
-
- * Set tab stops to 4 when editing this file.
- */
-
- #include "PriMan.h"
-
- /*
- * Process an input event from the main window.
- */
- void HandleMainWindow(ULONG class, WORD code, WORD key, WORD raw, UWORD shift, UWORD ctrl, struct Gadget *selectedGad)
- {
- int projectMenu, /* menu item selected from Project menu */
- taskMenu, /* menu item selected from Task menu */
- wide; /* slider is on wide scale */
-
- long signal = 0; /* signal type (SIGBREAKF_CTRL_C etc.) to send */
-
- UWORD menuNumber, /* ordinal menu number selected */
- gadget; /* ID of selected gadget */
-
- BYTE newpri; /* priority to change task to */
-
- BOOL frozen; /* task is frozen */
-
- struct MenuItem *item; /* menu item we're playing with */
-
- /*
- * The next few variables are used for creating and walking the list of
- * windows and screens belonging to the task we just killed.
- */
- ULONG ibase;
- struct List windowList;
- struct Node *node, *nextNode;
- struct Remember *windowKey;
- struct Screen *screen;
- struct Window *window;
-
- /*
- * Here are the requesters that can appear as the user plays around.
- * The first one is the About requester...
- */
- struct EasyStruct about =
- {
- sizeof(struct EasyStruct),
- 0,
- "About PriMan",
- "Task Priority Manager "VERSION"\nFreely Distributable\nCopyright 1993, 1994 Barry McConnell\nbmccnnll@tcd.ie",
- "Okay|Help..."
- };
-
- /*
- * When you try to Signal, Kill or Freeze PriMan, you get this requester.
- */
- struct EasyStruct suicide =
- {
- sizeof(struct EasyStruct),
- 0,
- "PriMan warning",
- "You can't do that to me!",
- "Whoops"
- };
-
- /*
- * User tried to manipulate a task which no longer exists.
- */
- struct EasyStruct lost =
- {
- sizeof(struct EasyStruct),
- 0,
- "PriMan trouble",
- "Task was not found!",
- "Okay"
- };
-
- /*
- * Confirmation to Break a task.
- */
- struct EasyStruct breakMsg =
- {
- sizeof(struct EasyStruct),
- 0,
- "PriMan warning",
- "Really signal `%s'?",
- "Signal|Cancel"
- };
-
- /*
- * Confirmation to Kill a task.
- */
- struct EasyStruct killMsg =
- {
- sizeof(struct EasyStruct),
- 0,
- "PriMan warning",
- "Really remove `%s'?",
- "Remove|Cancel"
- };
-
- /*
- * Confirmation to close a task's windows.
- */
- struct EasyStruct closeWindows =
- {
- sizeof(struct EasyStruct),
- 0,
- "PriMan query",
- "Close windows belonging\nto this task?",
- "Yes|No"
- };
-
- /*
- * Confirmation to close a task's screens.
- */
- struct EasyStruct closeScreens =
- {
- sizeof(struct EasyStruct),
- 0,
- "PriMan query",
- "Close screens belonging\nto this task?",
- "Yes|No"
- };
-
- /*
- * We come into this procedure after something has happened. We don't
- * leave until all necessary operations have been completed, and PriMan
- * is able to go back to sleep (or exit!).
- *
- * Having menus complicates things somewhat. The user can make multiple
- * selections, yet we only get one message. So we're going to handle
- * menus first (setting some action variables), followed by anything
- * else which only needs to be done once, before finally going through
- * a list of everything the user can possibly do, and checking to see
- * if it has been done (both by checking buttons and keypresses, and
- * examing the action variables set earlier).
- */
- projectMenu = taskMenu = -1; /* nothing selected so far */
- gadget = class == GADGETUP ? selectedGad -> GadgetID : NULL; /* if applicable, the gadget released */
-
- /*
- * Here we process the major once-off events.
- */
- switch (class)
- {
- /*
- * First up are menus. A design decision to be made is: how do we
- * handle multi-select? After a lot of consideration, I decided to
- * let the user select at most one item from each of the two menus.
- * The last item selected on each is the one we process. All others
- * (if any) are ignored, with the exception of Wide Slider, which
- * gets processed as soon as we come across it (even if this means
- * the slider jumps around many times in a row if the user multi-
- * selects it a lot in one go). The Frozen checkmark is carefully
- * set to what it should be later on in the code.
- *
- * Using the projectMenu and taskMenu variables, we keep track of
- * what we're going to process later. These simply contain copies
- * of the ordinal menu item selected.
- */
- case IDCMP_MENUPICK:
- menuNumber = code; /* first menu item selected */
- while (menuNumber != MENUNULL)
- {
- switch (MENUNUM(menuNumber))
- {
- case M_PROJECT:
- projectMenu = ITEMNUM(menuNumber);
- break;
-
- case M_TASK:
- switch (taskMenu = ITEMNUM(menuNumber))
- {
- case I_WIDE:
- /*
- * The easiest way to make sure the Wide
- * Slider menu item is always in the right
- * state is to update the slider right now.
- */
- WideSlider(ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_WIDE, NOSUB))
- -> Flags & CHECKED);
- break;
-
- case I_PRIORITY:
- /*
- * It's best to handle the Priority sub-
- * menu here, so we know later immediately
- * what priority to change to (stored in
- * the UserData field of the menu item).
- */
- newpri = (BYTE)GTMENUITEM_USERDATA(ItemAddress(menuStrip, menuNumber));
- break;
-
- case I_SIGNAL:
- /*
- * Here we just make a note of which signal
- * will need sending. Again, this info is
- * held in the menu item's UserData field.
- */
- signal = (long)GTMENUITEM_USERDATA(ItemAddress(menuStrip, menuNumber));
- break;
- }
- break;
- }
-
- /*
- * This is how we walk through the menu list in the order
- * the user selected them.
- */
- menuNumber = ItemAddress(menuStrip, menuNumber) -> NextSelect;
- }
- break;
-
- /*
- * If the user clicks the window's close gadget, we do the exact
- * same thing as if he selected the Hide menu item. Since only one
- * of these Intuition events can happen at once, it's safe to play
- * around with a variable that really belongs to the code above.
- */
- case IDCMP_CLOSEWINDOW:
- projectMenu = I_HIDE;
- break;
-
- /*
- * Obligatory stub when we get this event, to let Intuition know we
- * don't really care about window refreshing.
- */
- case IDCMP_REFRESHWINDOW:
- GT_BeginRefresh(mainWindow);
- GT_EndRefresh(mainWindow, TRUE);
- break;
-
- /*
- * There are a few things the user can do which have no menu
- * equivalents (or don't rely on projectMenu and taskMenu).
- * These get handled here.
- */
- default:
- /*
- * Handling the slider gadget is easy - we pretend the Priority
- * menu item was selected (see the comment for the CLOSEWINDOW
- * event above), and set newpri appropriately.
- */
- if (gadget == SLIDERGAD)
- {
- newpri = code;
- taskMenu = I_PRIORITY;
- }
-
- /*
- * The Wide Slider menu item is the only one that was actually
- * handled while scanning through the selected menu items. But
- * the user can also press the Tab key to change the scale, and
- * we handle that here. If the Wide Slider menu item is enabled
- * (disabled means either no task has been selected, or the
- * current priority is already in the wide range), we need to
- * toggle it, as well as updating the slider gadget.
- */
- item = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_WIDE, NOSUB));
- if (key == TAB && item -> Flags & ITEMENABLED)
- {
- WideSlider(item -> Flags & CHECKED ? FALSE : TRUE);
- ClearMenuStrip(mainWindow);
- item -> Flags ^= CHECKED;
- ResetMenuStrip(mainWindow, menuStrip);
- }
-
- /*
- * To allow PriMan to be fully keyboard-navigatiable, we let
- * the user move through the task list using the up and down
- * cursor keys, and change priorites (assuming a task is
- * currently selected) using the left and right cursor keys.
- * Holding down the Shift key moves to the extremity of the
- * task list or slider. We actually cheat here, and fake a
- * message saying that a certain ListView entry was selected,
- * so that the next section of code (below) will handle
- * everything for us!
- */
- if (raw) /* was a cursor key pressed? */
- {
- /*
- * If the user presses the cursor left or right keys, we'll
- * need to know whether we're on a wide or narrow priority
- * scale, so we figure that out now by looking at the menu.
- */
- wide = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_WIDE, NOSUB)) -> Flags & CHECKED;
-
- switch (raw)
- {
- case CURSORUP:
- /*
- * The first thing we do is always to pretend that
- * the ListView gadget itself has been clicked on.
- */
- gadget = LISTGAD;
-
- /*
- * If the Shift key has been pressed, we always
- * jump to the first entry in the list.
- */
- if (shift)
- code = 0;
-
- else
- /*
- * We need to handle two special cases here: if
- * no entry is selected, we select the first
- * one; and if the first entry is selected, we
- * do nothing.
- */
- switch (pos)
- {
- case -1: /* no entry selected */
- code = 0;
- break;
-
- case 0: /* first entry selected */
- gadget = 0; /* gadget wasn't really clicked on */
- break;
-
- default:
- code = pos - 1;
- }
- break;
-
- case CURSORDOWN:
- /*
- * This is pretty similar to the previous case,
- * except we do nothing if the last entry is
- * selected, and jump to the last entry if Shift is
- * pressed.
- */
- gadget = LISTGAD;
-
- if (shift)
- code = taskCount - 1;
- else if (pos == -1) /* no entry selected */
- code = 0;
- else if (pos == taskCount - 1) /* last entry selected */
- gadget = 0; /* gadget wasn't really clicked on */
- else
- code = pos + 1;
- break;
-
- case CURSORLEFT:
- /*
- * Now we're working with the slider gadget. If no
- * task is selected, we do nothing here. Otherwise,
- * we check for the shift key as before, and jump
- * to the extremity of the current scale if
- * necessary. If it wasn't pressed, we decrement
- * the priority as long as it will stay within the
- * range of the current scale. We also pretend that
- * the Priority menu item was selected, and leave
- * it up to some code later to handle this.
- */
- if (current)
- {
- taskMenu = I_PRIORITY;
-
- if (shift)
- newpri = wide ? -128 : -25;
- else if (current -> ln_Pri > (wide ? -128 : -25))
- newpri = current -> ln_Pri - 1;
- else
- taskMenu = -1; /* don't want to change priority after all */
- }
- break;
-
- case CURSORRIGHT:
- /*
- * Very similar to the above case, except we're
- * incrementing and not decrementing.
- */
- if (current)
- {
- taskMenu = I_PRIORITY;
-
- if (shift)
- newpri = wide ? 127 : 25;
- else if (current -> ln_Pri < (wide ? 127 : 25))
- newpri = current -> ln_Pri + 1;
- else
- taskMenu = -1; /* don't want to change priority after all */
- }
- break;
- }
- }
-
- /*
- * If the user clicks on an entry in the list, we have to do
- * quite a lot of work. First, we update the slider gadget to
- * reflect the priority and move it to the appropriate scale
- * (narrow if possible). Next, we enable various gadgets and
- * menu items which are not accessible while no task is
- * selected. And finally, we adjust the Frozen menu item
- * checkmark to match the task's state.
- */
- if (gadget == LISTGAD)
- {
- /*
- * We must make a note of the ordinal number of the
- * currently-selected entry in the ListView, and then walk
- * through all the entries in the list until we get to that
- * ordinal position. It's okay to trash the code variable
- * in this loop since we won't need it later.
- */
- pos = code;
- for (current = taskList.lh_Head; code > 0; current = current -> ln_Succ, code --)
- ; /* empty body */
- currentTask = ((struct ListType *)current) -> mainTask;
-
- /*
- * If the user is manipulating the ListView using the
- * keyboard, we'll need to actually select the relevant
- * entry here (passed down from the cursor-key code above).
- * Under V37 we always put this entry at the top of the
- * list, but under V39 we can use the MakeVisible tag to
- * scroll the ListView when it becomes necessary, which is
- * more user-friendly.
- */
- if (raw)
- GT_SetGadgetAttrs(listGad, mainWindow, NULL,
- GTLV_Selected, pos,
- osver < 39 ? GTLV_Top : GTLV_MakeVisible, pos,
- TAG_END);
-
- /*
- * A separate function makes the necessary changes to the
- * slider gadget and enables the relevant menu items.
- */
- OnTask();
-
- /*
- * Here we simply enable the Break and Kill buttons.
- */
- GT_SetGadgetAttrs(breakGad, mainWindow, NULL,
- GA_Disabled, FALSE,
- TAG_END);
- GT_SetGadgetAttrs(killGad, mainWindow, NULL,
- GA_Disabled, FALSE,
- TAG_END);
-
- /*
- * This last piece of code adjusts the Frozen menu item
- * checkmark appropriately, by reading its address and
- * inspecting the task structure to find out what it
- * should be set to. The menu strip is removed from the
- * window while the changes are being made.
- */
- ClearMenuStrip(mainWindow);
-
- item = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_FROZEN, NOSUB));
- if (Frozen(currentTask))
- item -> Flags |= CHECKED;
- else
- item -> Flags &= ~CHECKED;
-
- ResetMenuStrip(mainWindow, menuStrip);
- }
- break;
- }
-
- /*
- * Once we've got this far, we have sorted out what menu events need
- * handling, updated the window's gadgetry to reflect the currently-
- * selected task, and are left with the possibility of one or both of
- * projectMenu and taskMenu set. We'll handle the latter first, since
- * the former may require PriMan's interface to be closed down. In
- * actual fact, what we do now is group all the different events that
- * result in the same action (keypress, mouse click, menu selection),
- * and handle them with one piece of code.
- *
- * The first thing we'll do is handle the Update menu item, which also
- * has Space and Return keyboard shortcuts. This one is trivial...
- */
- if (taskMenu == I_UPDATE || key == ' ' || key == RETURN)
- CreateList(&memoryKey);
-
- /*
- * We handle the Priority menu item here. In actual fact, there are
- * several places above where taskMenu gets set to this, but what it
- * amounts to is: as long as the task is still valid, its priority gets
- * changed to newpri.
- */
- if (taskMenu == I_PRIORITY)
- {
- if (!ValidTask(currentTask))
- {
- SimpleRequest(&lost, NULL);
- CreateList(&memoryKey);
- }
- else
- {
- /*
- * Changing the priority involves more than just telling Exec
- * to update it. We also remove the list from the ListView,
- * make a change to the appropriate entry (using CreateString()
- * which will join the task's name and new priority together;
- * we instruct it to overwrite what was originally there),
- * restore the ListView, and finally update the slider gadget.
- */
- SetTaskPri(currentTask, newpri);
-
- GT_SetGadgetAttrs(listGad, mainWindow, NULL,
- GTLV_Labels, ~0,
- TAG_END);
- CreateString(currentTask, current -> ln_Name);
- current -> ln_Pri = newpri;
- GT_SetGadgetAttrs(listGad, mainWindow, NULL,
- GTLV_Labels, &taskList,
- GTLV_Selected, pos, /* only required under 2.x */
- TAG_END);
-
- OnTask();
- }
- }
-
- /*
- * The Break and Kill options are very similar, at least to start with,
- * so they share some code. Since there are many scenarios involving
- * the Break option, we check out that first. If the user used the menu
- * here, it's easy, since the appropriate signal type is already in the
- * signal variable. Otherwise, we must check the following:
- *
- * - If the Ctrl key is pressed, set the signal variable if a valid
- * keyboard shortcut is being used.
- *
- * - If the Break gadget was pressed, or its own keyboard shortcut was
- * used, set the signal variable for a Ctrl-C signal.
- */
- if (ctrl && key)
- /*
- * Signals can be sent to a task simply by holding down the Ctrl
- * key and pressing C, D, E, or F, and we set the signal variable
- * appropriately here. Note that the taskMenu variable is actually
- * not used by the Break code: if signal is set, it is assumed that
- * taskMenu is too. (So we don't actually need to explicitly set
- * taskMenu to I_SIGNAL below!)
- */
- switch (key)
- {
- case 'c':
- signal = SIGBREAKF_CTRL_C;
- break;
-
- case 'd':
- signal = SIGBREAKF_CTRL_D;
- break;
-
- case 'e':
- signal = SIGBREAKF_CTRL_E;
- break;
-
- case 'f':
- signal = SIGBREAKF_CTRL_F;
- break;
- }
-
- else if (gadget == BREAKGAD || key == 'b')
- signal = SIGBREAKF_CTRL_C;
-
- /*
- * We continue on if (somehow) it looks like the Signal submenu was
- * used, or if the Kill function has been activated. In both cases, we
- * make sure a task actually has been selected.
- */
- if ((signal || taskMenu == I_KILL || gadget == KILLGAD || key == 'k') && current)
- {
- /*
- * Since there are many keyboard shortcuts leading here, we visibly
- * depress the appropriate gadget if necessary.
- */
- if (key)
- PressGadget(mainWindow, signal ? breakGad : killGad);
-
- /*
- * First we need to make sure the task is still valid, and that it
- * is not our own task!
- */
- BusyPointer();
-
- if (!ValidTask(currentTask))
- {
- SimpleRequest(&lost, NULL);
- CreateList(&memoryKey);
- }
-
- else if (currentTask == FindTask(NULL))
- SimpleRequest(&suicide, NULL);
-
- else
- {
- /*
- * We're only allowed to continue on if:
- *
- * - The Confirm Actions setting is not set, or;
- *
- * - The Shift key is held down, or;
- *
- * - The above two are false and the user answers Okay to a
- * confirmation requester.
- *
- * This line of code makes use of the fact that C only
- * evaluates as much of a sequence of logical expressions as is
- * necessary to determine if the outcome will be true or false.
- */
- if (!confirm || shift ||
- SimpleRequest(signal ? &breakMsg : &killMsg, currentTask -> tc_Node.ln_Name))
- {
- /*
- * At this point, it is possible that the target task has
- * quietly exited. We handled that case at the start and
- * notified the user appropriately, but here we'll just let
- * it go and consider the job done. We'll disable task
- * switching now and do the second check - we couldn't just
- * have one check earlier since it wouldn't be possible to
- * disable task switching while the confirmation requester
- * was open!
- */
- Forbid();
- if (ValidTask(currentTask))
- {
- if (signal)
- {
- /*
- * This is where the two functions differ. For
- * Break, we send a certain signal to the task, and
- * then wait around for half a second, giving it
- * time to exit before we update the ListView.
- */
- Signal(currentTask, signal);
- Permit();
- Delay(BreakDelay);
- }
- else
- {
- /*
- * This is the Kill code. (Note that we're still
- * inside a Forbid().) The very first thing we do
- * is kill the task, before re-enabling task
- * switching...
- */
- RemTask(currentTask); /* BLAM! */
- Permit();
-
- /*
- * Some tricky stuff follows. We want to find out
- * if the task had any windows or screens open.
- * This is done by walking through all windows on
- * all screens, checking for message reply-ports
- * pointing to the task. We build a linked list of
- * these windows (since there could potentially be
- * many of them), use a Remember structure (as with
- * the task list) for the nodes, and lock
- * IntuitionBase so that no window operations can
- * happen while we traverse the lists.
- */
- windowList.lh_Head = (struct Node *)&windowList.lh_Tail;
- windowList.lh_Tail = NULL;
- windowList.lh_TailPred = (struct Node *)&windowList.lh_Head;
- windowKey = NULL;
- ibase = LockIBase(NULL);
-
- /*
- * The outer loop walks through all the screens...
- */
- for (screen = IntuitionBase -> FirstScreen; screen; screen = screen -> NextScreen)
-
- /*
- * The inner loop is for windows...
- */
- for (window = screen -> FirstWindow; window; window = window -> NextWindow)
-
- /*
- * We have a match if the window has a
- * valid UserPort, and the task referenced
- * there is the one we killed above.
- */
- if (window -> UserPort && (struct Task *)(window -> UserPort -> mp_SigTask) == currentTask)
-
- /*
- * For a match, we allocate memory for
- * a new Node structure to store a
- * pointer to the window. (If this
- * memory allocation fails, we'll just
- * move onto the next window - missing
- * out a window is the least of our
- * worries if we're *that* low on free
- * memory!) While the Node structure is
- * supposed to contain a pointer to
- * text, we cheat a little and stick in
- * a pointer to the Window structure.
- */
- if (node = AllocRemember(&windowKey, sizeof(struct Node), MEMF_ANY))
- {
- node -> ln_Name = (char *)window;
- AddTail(&windowList, node);
- }
-
- /*
- * At the end of the loops, we have a list of any
- * windows the task had open. Assuming this list is
- * not empty, we ask the user if he'd like to close
- * them. Since we can't leave IntuitionBase locked
- * from now on (a requester is being opened!), we
- * simply have to hope the windows don't get closed
- * behind our backs, as then the machine will crash
- * when we go to close them... (Sorry, I can't see
- * any way around this!)
- */
- UnlockIBase(ibase);
- if (windowList.lh_Head -> ln_Succ) /* at least one entry here */
- {
- if (SimpleRequest(&closeWindows, NULL))
- {
- /*
- * Now we go round in a loop closing all
- * the windows on the list. As we do so,
- * we remove the Node structures, *unless*
- * the window is the last on its screen, in
- * which case we leave the Node intact, but
- * replace its data with the screen pointer
- * instead - see below for why!
- */
- node = windowList.lh_Head;
- while (nextNode = node -> ln_Succ)
- {
- screen = (window = (struct Window *)(node -> ln_Name)) -> WScreen;
- CloseWindowSafely(window);
- if (screen -> FirstWindow) /* more left on this screen */
- Remove(node);
- else
- node -> ln_Name = (char *)screen;
-
- /*
- * "node" is probably invalid (freed)
- * down here, but we got a copy of its
- * successor earlier...
- */
- node = nextNode;
- }
-
- /*
- * At this point, if the list is not empty,
- * it contains pointers to empty screens.
- * So we ask the user if he wants to close
- * these too, on the (reasonable)
- * assumption that they belong to the task
- * we killed.
- */
- if (windowList.lh_Head -> ln_Succ) /* at least one entry here */
- if (SimpleRequest(&closeScreens, NULL))
- FORLIST(&windowList, node)
- CloseScreen((struct Screen *)(node -> ln_Name));
- }
- }
-
- /*
- * Down here, we've finished playing about with
- * window pointers, so we can free up the list.
- */
- FreeRemember(&windowKey, TRUE);
- }
- }
-
- /*
- * At the end of it all, we update the list to reflect what
- * happened.
- */
- CreateList(&memoryKey);
- }
- }
- NormalPointer();
- }
-
- /*
- * Here is the code for the Frozen menu item. It actually consists of
- * some "heavy magic". We create two new Exec task states for frozen
- * tasks: one to say "frozen while sleeping" and the other to say
- * "frozen while awake" (so we know whether the task needs to run when
- * melted). Since these states are actually invalid, Exec will ignore
- * the frozen tasks. All this has to be done while interrupts are
- * disabled, since we might be moving tasks between lists.
- */
- if (taskMenu == I_FROZEN)
- {
- Disable();
-
- /*
- * As with the Break and Kill code, we must make sure that the task
- * is still valid, and is not us!
- */
- if (!ValidTask(currentTask))
- {
- Enable();
- SimpleRequest(&lost, NULL);
- CreateList(&memoryKey);
- }
-
- else if (currentTask == FindTask(NULL))
- {
- Enable();
- SimpleRequest(&suicide, NULL);
- }
-
- else
- {
- /*
- * We look at the state of the Frozen menu item, and if it's
- * checked, continue if the task has not already been frozen.
- */
- if (ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_FROZEN, NOSUB)) -> Flags & CHECKED)
- {
- if (!Frozen(currentTask))
-
- /*
- * Freezing the task depends on what state it's in.
- * Tasks waiting for CPU time (TS_READY) must get put
- * on the "sleeping" list, as well as changing their
- * state.
- */
- if (currentTask -> tc_State & TS_READY)
- {
- Remove((struct Node *)currentTask);
- Enqueue(&(SysBase -> TaskWait), (struct Node *)currentTask);
- currentTask -> tc_State = FROZENREADY;
- }
- else
- currentTask -> tc_State = FROZEN;
- }
-
- /*
- * Task must be melted. Again, there are two cases: if it's
- * ready to run, we must move it back onto its original list
- * before changing its state; if it's sleeping, we send it a
- * signal based on the contents of its "received signals" field
- * (if a signal arrived while it was frozen, it would not have
- * been woken up since it was in an invalid state!).
- */
- else if (currentTask -> tc_State == FROZENREADY)
- {
- Remove((struct Node *)currentTask);
- Enqueue(&(SysBase -> TaskReady), (struct Node *)currentTask);
- currentTask -> tc_State = TS_READY;
- }
- else if (currentTask -> tc_State == FROZEN)
- {
- currentTask -> tc_State = TS_WAIT;
- Signal(currentTask, currentTask -> tc_SigRecvd);
- }
-
- Enable();
-
- /*
- * Down here, we update the ListView to reflect the state of
- * the task, in a similar way to when we changed its priority
- * above.
- */
- GT_SetGadgetAttrs(listGad, mainWindow, NULL,
- GTLV_Labels, ~0,
- TAG_END);
- CreateString(currentTask, current -> ln_Name);
- GT_SetGadgetAttrs(listGad, mainWindow, NULL,
- GTLV_Labels, &taskList,
- GTLV_Selected, pos, /* only required under 2.0 */
- TAG_END);
- }
- }
-
- /*
- * It is possible for the user to play around with the Frozen menu item
- * without us noticing (e.g. it's followed by another menu selection,
- * and so didn't get handled above). We do a quick check to see if the
- * checkmark has got out of sync with the state of the task (assuming
- * one has been selected!), and if so, put it back to the way it should
- * be. (We could just update the checkmark all the time, but that would
- * be wasteful since almost every time HandleMainWindow() was called we
- * would be modifying the menu!)
- */
- else if (current)
- {
- item = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_FROZEN, NOSUB));
- frozen = Frozen(currentTask);
-
- switch ((item -> Flags & CHECKED) != 0)
- {
- case TRUE:
- if (!frozen)
- {
- ClearMenuStrip(mainWindow);
- item -> Flags &= ~CHECKED;
- ResetMenuStrip(mainWindow, menuStrip);
- }
- break;
-
- case FALSE:
- if (frozen)
- {
- ClearMenuStrip(mainWindow);
- item -> Flags |= CHECKED;
- ResetMenuStrip(mainWindow, menuStrip);
- }
- break;
- }
- }
-
- /*
- * Now we're onto the Project menu, and first up is opening the
- * Settings window. If it's already open, we move it to the front.
- */
- if (gadget == SETTINGSGAD || projectMenu == I_SETTINGS || key == 's')
- {
- if (key)
- PressGadget(mainWindow, setGad);
-
- if (setWindow)
- {
- WindowToFront(setWindow);
- ActivateWindow(setWindow);
- }
- else
- OpenSettingsWindow();
- }
-
- /*
- * Here is the rather trivial code for the About menu item. If the user
- * selects the Help button in the requester, we open up the Help file.
- */
- if (projectMenu == I_ABOUT)
- if (!SimpleRequest(&about, NULL))
- Help();
-
- /*
- * If the Help menu item is selected, or the Help key is pressed, we
- * also open up the Help file.
- */
- if (projectMenu == I_HELP || raw == HELP)
- Help();
-
- /*
- * The code for the Hide menu item just calls the Hide() function,
- * which either commences the exit routine, or else closes all the
- * windows and causes PriMan to sit quietly in the background.
- */
- if (projectMenu == I_HIDE)
- Hide();
-
- /*
- * Quitting is trivial - we just need to assert ALL_OKAY!
- */
- if (projectMenu == I_QUIT || key == ESCAPE)
- error = ALL_OKAY;
- }
-
-
- /*
- * Process an input event from the settings window. We have to be careful
- * about which gadget page is showing!
- */
- void HandleSettingsWindow(ULONG class, WORD code, WORD key, WORD raw, UWORD shift, struct Gadget *selectedGad)
- {
- /*
- * When writing out the ToolTypes, we need the following:
- *
- * oldTools = pointer to existing ToolType array (which itself contains
- * pointers to the ToolTypes themselves)
- *
- * newTools = array of pointers to new ToolType array
- *
- * empty = array of strings making up the standard ToolTypes,
- * pointed to from newTools
- *
- * string = existing ToolType we're currently working on
- */
- char **oldTools,
- *newTools[MaxToolTypes],
- empty[MaxStdTools][MAXFONTNAME + MaxToolName],
- *string;
-
- int oldPos, /* current position in original ToolType array */
- newPos; /* current position in our new ToolType array */
-
- UWORD gadget; /* gadget that was pressed */
- WORD newPage; /* settings page we're going to move to */
- BYTE toolpri; /* PriMan's task priority */
- BOOL redraw = FALSE; /* main window will need redrawing later */
-
- gadget = class == GADGETUP ? selectedGad -> GadgetID : 0;
-
- /*
- * First we handle events that can happen regardless of which gadget
- * page is showing. The REFRESHWINDOW event is slightly more complex
- * than with the main window, because the line around the gadgets needs
- * to get redrawn (since it isn't a GadTools gadget, which refresh
- * themselves).
- */
- if (class == IDCMP_REFRESHWINDOW)
- {
- GT_BeginRefresh(setWindow);
- DrawSettingsBox();
- GT_EndRefresh(setWindow, TRUE);
- }
-
- /*
- * As before, pressing the Escape key immediately aborts.
- */
- else if (key == ESCAPE)
- error = ALL_OKAY;
-
- /*
- * If the Help key is pressed, we open up the Help file.
- */
- else if (raw == HELP)
- Help();
-
- /*
- * If the Page cycle gadget is clicked, we just move onto the new page.
- */
- else if (gadget == PAGEGAD)
- NewPage(code);
-
- /*
- * The keyboard shortcut requires a bit more work. We need to update
- * the Page cycle gadget ourselves, and also check to see if the Shift
- * key is being pressed, before moving onto the new page.
- */
- else if (key == 'p')
- {
- newPage = Step(page, 2, shift);
- GT_SetGadgetAttrs(pageGad, setWindow, NULL,
- GTCY_Active, newPage,
- TAG_END);
- NewPage(newPage);
- }
-
- /*
- * Save and Use are pretty similar, and share a lot of code. Note that
- * we don't do anything if the user presses S for Save but PriMan's
- * .info file cannot be found, since there is no icon to write out the
- * ToolTypes to!
- */
- else if (gadget == SAVEGAD || (key == 's' && myIcon) ||
- gadget == USEGAD || key == 'u')
- {
- if (key)
- PressGadget(setWindow, key == 's' ? saveGad : useGad);
-
- BusyPointer(); /* there may be a delay writing out to disk */
-
- /*
- * Now we start copying all the temporary settings variables into
- * the master variables. If the user did something like change the
- * window type or font, we'll need to redraw the window later, so
- * we use another variable to remind us about that.
- *
- * First up are the Commodity settings. We update the hotkey
- * variable directly from the Hotkey string gadget, and the
- * priority from the Priority integer gadget.
- */
- strcpy(hotkey, ((struct StringInfo *)hotkeyGad -> SpecialInfo) -> Buffer);
- priority = ((struct StringInfo *)priorityGad -> SpecialInfo) -> LongInt;
-
- /*
- * If PriMan was already running as a Commodity, we'll remove it.
- * Then if it still needs to run as a Commodity, it gets added back
- * in. This effectively changes its hotkey and priority, without
- * us having to go to the effort of checking to see if this really
- * needs to get done. :-)
- */
- if (commodity)
- {
- DeleteCxObjAll(broker);
- broker = NULL;
- }
-
- if (tempCommodity)
- SetupCommodity();
-
- /*
- * If the window type has been changed, we make a note that we'll
- * need to redraw ourselves.
- */
- if (refresh != tempRefresh)
- {
- refresh = tempRefresh;
- redraw = TRUE;
- }
-
- /*
- * If the user has changed the Confirm Actions setting, we need to
- * add or remove "..." after the Kill and Signal menu items.
- */
- if (confirm != tempConfirm)
- {
- confirm = tempConfirm;
- MenuEllipsis(menuStrip, TRUE);
- }
-
- /*
- * We'll copy over the rest of the boolean variables now.
- */
- open = tempOpen;
- iconify = tempIconify;
- commodity = tempCommodity;
- popup = tempPopup;
-
- /*
- * This is the priority the user has entered in the Task Priority
- * integer gadget. Since it is this one that's going to get used,
- * we check against the current priority PriMan is running at, and
- * if that's different, change it and update the ListView to
- * reflect the new priority (this is easier than just finding our
- * own entry and changing that!).
- */
- toolpri = ((struct StringInfo *)toolpriGad -> SpecialInfo) -> LongInt;
- if (FindTask(NULL) -> tc_Node.ln_Pri != toolpri)
- {
- SetTaskPri(FindTask(NULL), toolpri);
- CreateList(&memoryKey);
- }
-
- /*
- * If the Gadget Font requester was opened, and the new font is not
- * the same as the old one, we copy it across and make a note to
- * redraw the window.
- */
- if (newPropFont && (strcmp((propFontReq -> fo_Attr).ta_Name, propName)
- || !((propFontReq -> fo_Attr).ta_YSize == propTA.ta_YSize)))
- {
- strcpy(propName, (propFontReq -> fo_Attr).ta_Name);
- propTA.ta_YSize = (propFontReq -> fo_Attr).ta_YSize;
- redraw = TRUE;
- }
-
- /*
- * Ditto for the List Font.
- */
- if (newMonoFont && (strcmp((monoFontReq -> fo_Attr).ta_Name, monoName)
- || !((monoFontReq -> fo_Attr).ta_YSize == monoTA.ta_YSize)))
- {
- strcpy(monoName, (monoFontReq -> fo_Attr).ta_Name);
- monoTA.ta_YSize = (monoFontReq -> fo_Attr).ta_YSize;
- redraw = TRUE;
- }
-
- /*
- * Finally, the user may have changed the Iconify or Commodity
- * options, meaning the Hide menu item needs enabling or disabling.
- * We handle that here. If PriMan can either be iconified or run in
- * the background as a Commodity, it gets enabled; otherwise, it is
- * disabled.
- */
- if (iconify || commodity)
- OnMenu(mainWindow, FULLMENUNUM(M_PROJECT, I_HIDE, NOSUB));
- else
- OffMenu(mainWindow, FULLMENUNUM(M_PROJECT, I_HIDE, NOSUB));
-
- /*
- * Here is where the Save and Use options differ. The former
- * requires writing everything out to disk. We already have a
- * pointer to PriMan's .info file (from when we read the ToolTypes
- * in the first place), and we reuse that.
- */
- if (gadget == SAVEGAD || key == 's')
- {
- /*
- * Within the .info file is a pointer to the ToolTypes. We
- * make a copy of this in oldTool so it can be restored
- * later, and use it to walk through the ToolTypes.
- */
- oldTools = myIcon -> do_ToolTypes;
-
- /*
- * What we want to do is create a new array of ToolTypes,
- * containing all the "standard" ones, along with any
- * additional ones the user may have put in. The way we do
- * this is: we walk through the original array, copy the
- * pointers to unrecognised ToolTypes into our new array,
- * and then tag on the standard ToolTypes. Essentially we
- * are skipping over the standard ToolTypes in the original
- * array, as they are going to be replaced later.
- *
- * The alternative would be to simply overwrite anything
- * already in the array, but that's not entirely user-
- * friendly... (If the user has added ToolTypes manually since
- * starting PriMan, they'll get overwritten anyway, but that's
- * just tough!)
- *
- * The loop continues through the original array until it
- * encounters a NULL pointer. Each iteration does a string
- * comparison against the standard ToolTypes, and if none
- * of them match, a pointer to the string is copied over.
- * The loop can end prematurely if we run out of space in
- * the array to hold everything. (The array can hold
- * MaxToolTypes entries, of which we'll be using
- * MaxStdTools entries, plus one for the NULL pointer at
- * the end.)
- */
- oldPos = newPos = 0;
-
- while ((string = oldTools[oldPos++]) && newPos < MaxToolTypes - MaxStdTools - 1)
- if (strncmp(string, "COMMODITY", 9) && strncmp(string, "CX_POPUP", 8)
- && strncmp(string, "CX_POPKEY", 9) && strncmp(string, "CX_PRIORITY", 11)
- && strncmp(string, "LEFT", 4) && strncmp(string, "TOP", 3)
- && strncmp(string, "WIDTH", 5) && strncmp(string, "HEIGHT", 6)
- && strncmp(string, "GADFONT", 7) && strncmp(string, "GADSIZE", 7)
- && strncmp(string, "LISTFONT", 8) && strncmp(string, "LISTSIZE", 8)
- && strncmp(string, "REFRESH", 7) && strncmp(string, "SCREEN", 6)
- && strncmp(string, "ICONLEFT", 8) && strncmp(string, "ICONTOP", 7)
- && strncmp(string, "CONFIRM", 7) && strncmp(string, "ICONIFY", 7)
- && strncmp(string, "TOOLPRI", 7)) /* get rid of our ones */
- newTools[newPos++] = string;
-
- /*
- * Now for the standard ToolTypes. We build this up in a
- * separate array, which has plenty of space allocated for
- * it to hold the strings.
- */
- sprintf(empty[0], "COMMODITY=%s", commodity ? "YES" : "NO");
- sprintf(empty[1], "CX_POPUP=%s", popup ? "YES" : "NO");
- sprintf(empty[2], "CX_POPKEY=%s", hotkey);
- sprintf(empty[3], "CX_PRIORITY=%ld", priority);
- sprintf(empty[4], "LEFT=%ld", mainWindow -> LeftEdge);
- sprintf(empty[5], "TOP=%ld", mainWindow -> TopEdge);
- sprintf(empty[6], "WIDTH=%ld", mainWindow -> Width);
- sprintf(empty[7], "HEIGHT=%ld", mainWindow -> Height);
- sprintf(empty[8], "GADFONT=%s", propName);
- sprintf(empty[9], "GADSIZE=%ld", propTA.ta_YSize);
- sprintf(empty[10], "LISTFONT=%s", monoName);
- sprintf(empty[11], "LISTSIZE=%ld", monoTA.ta_YSize);
- sprintf(empty[12], "REFRESH=%s", refresh == SIMPLEWINDOW ? "SIMPLE" : "SMART");
- sprintf(empty[13], "SCREEN=%s", open == FRONTSCREEN ? "FRONT" : "DEFAULT");
- sprintf(empty[14], "ICONLEFT=%ld", iconLeft);
- sprintf(empty[15], "ICONTOP=%ld", iconTop);
- sprintf(empty[16], "CONFIRM=%s", confirm ? "YES" : "NO");
- sprintf(empty[17], "ICONIFY=%s", iconify ? "YES" : "NO");
- sprintf(empty[18], "TOOLPRI=%ld", toolpri);
-
- /*
- * The string pointers in the above array get copied into
- * newTools, starting just after where we put the last non-
- * standard ToolType. (oldPos gets reused here.)
- */
- for (oldPos = 0; oldPos < MaxStdTools;)
- newTools[newPos++] = empty[oldPos++];
-
- /*
- * Finally, we put a NULL pointer after the last entry.
- */
- newTools[newPos] = NULL;
-
- /*
- * Now we're ready to write out the .info file again. We
- * first replace the pointer to the original ToolType array
- * with our new one.
- */
- myIcon -> do_ToolTypes = newTools;
- PutDiskObject(myName, myIcon);
-
- /*
- * Before ending, we restore the pointer to the original
- * ToolType array, as it will be freed later (when PriMan
- * exits).
- */
- myIcon -> do_ToolTypes = oldTools;
- }
-
- NormalPointer();
-
- /*
- * Down here, all the settings have been updated, and - if
- * necessary - written out to disk. So we close the Settings
- * window, and then (if required) redraw the main window.
- */
- CloseSettingsWindow();
- if (redraw)
- {
- CloseMainWindow();
- OpenMainWindow();
- }
- }
-
- /*
- * Handling the Cancel gadget is much easier. (Note that even though
- * the Settings window does not have a close gadget, it can still
- * receive the corresponding event through - say - WindowDaemon.)
- */
- else if (gadget == CANCELGAD || key == 'c' || class == IDCMP_CLOSEWINDOW)
- {
- if (key)
- PressGadget(setWindow, cancelGad);
-
- /*
- * Since the master settings variables have not been changed, all
- * we need to do is close the window!
- */
- CloseSettingsWindow();
- }
-
- /*
- * Now we're onto the page-specific gadgets. We only want to handle the
- * ones pertaining to the currently-selected page. (For example, the
- * same keypress can have a different meaning depending on the page
- * being shown.)
- */
- else switch(page)
- {
- case 0:
- /*
- * The Interface page. First up is the Gadget Font button. A
- * separate function handles most of this for us.
- */
- if (gadget == GFONTGAD || key == 'g')
- {
- if (key)
- PressGadget(setWindow, propFontButGad);
-
- newPropFont = RequestFont(&propFontReq, monoFontReq, tempPropName, &tempPropSize,
- propFontGad, propString, "Select Gadget Font", 0);
- }
-
- /*
- * The List Font button is handled in exactly the same manner,
- * except it only allows fixed-width fonts.
- */
- else if (gadget == LFONTGAD || key == 'l')
- {
- if (key)
- PressGadget(setWindow, monoFontButGad);
-
- newMonoFont = RequestFont(&monoFontReq, propFontReq, tempMonoName, &tempMonoSize,
- monoFontGad, monoString, "Select List Font", FOF_FIXEDWIDTHONLY);
- }
-
- /*
- * Handling the Window Type gadget is easy if the user clicks
- * on the cycle gadget - we just copy across the new position.
- */
- else if (gadget == REFRESHGAD)
- tempRefresh = code;
-
- /*
- * The keyboard shortcut requires us to change the cycle gadget
- * ourselves, and toggle the settings variable.
- */
- else if (key == 'w')
- {
- tempRefresh = !tempRefresh;
- GT_SetGadgetAttrs(refreshGad, setWindow, NULL,
- GTCY_Active, tempRefresh,
- TAG_END);
- }
-
- /*
- * The code for the Open On gadget is similar...
- */
- else if (gadget == OPENGAD)
- tempOpen = code;
-
- else if (key == 'o')
- {
- tempOpen = !tempOpen;
- GT_SetGadgetAttrs(openGad, setWindow, NULL,
- GTCY_Active, tempOpen,
- TAG_END);
- }
-
- break;
-
- case 1:
- /*
- * The Commodity page. First up is the Install checkbox.
- * Updating this is similar to the cycle gadgets above, except
- * we must also enable or disable the rest of the gadgets on
- * this page, based on the new setting.
- */
- if (gadget == COMGAD || key == 'i')
- {
- if (gadget == COMGAD)
- tempCommodity = selectedGad -> Flags & GFLG_SELECTED;
- else
- {
- tempCommodity = !tempCommodity;
- GT_SetGadgetAttrs(comGad, setWindow, NULL,
- GTCB_Checked, tempCommodity,
- TAG_END);
- }
-
- GT_SetGadgetAttrs(popupGad, setWindow, NULL,
- GA_Disabled, !tempCommodity,
- TAG_END);
- GT_SetGadgetAttrs(hotkeyGad, setWindow, NULL,
- GA_Disabled, !tempCommodity,
- TAG_END);
- GT_SetGadgetAttrs(priorityGad, setWindow, NULL,
- GA_Disabled, !tempCommodity,
- TAG_END);
- }
-
- /*
- * Again, the Popup checkbox is handled similarly...
- */
- else if (gadget == POPUPGAD)
- tempPopup = selectedGad -> Flags & GFLG_SELECTED;
-
- else if (key == 'l' && tempCommodity)
- {
- tempPopup = !tempPopup;
- GT_SetGadgetAttrs(popupGad, setWindow, NULL,
- GTCB_Checked, tempPopup,
- TAG_END);
- }
-
- /*
- * The hotkey for the string and integer gadgets simply places
- * the cursor inside them.
- */
- else if (key == 'h' && tempCommodity)
- ActivateGadget(hotkeyGad, setWindow, NULL);
-
- else if (key == 'y' && tempCommodity)
- ActivateGadget(priorityGad, setWindow, NULL);
-
- break;
-
- case 2:
- /*
- * The General page. It works in a similar way to before - the
- * hotkey for the integer gadget places the cursor inside it,
- * and the checkboxes get toggled on their hotkeys.
- */
- if (key == 'y')
- ActivateGadget(toolpriGad, setWindow, NULL);
-
- else if (gadget == CONFIRMGAD)
- tempConfirm = selectedGad -> Flags & GFLG_SELECTED;
-
- else if (key == 'a')
- {
- tempConfirm = !tempConfirm;
- GT_SetGadgetAttrs(confirmGad, setWindow, NULL,
- GTCB_Checked, tempConfirm,
- TAG_END);
- }
-
- else if (gadget == ICONIFYGAD)
- tempIconify = selectedGad -> Flags & GFLG_SELECTED;
-
- else if (key == 'i')
- {
- tempIconify = !tempIconify;
- GT_SetGadgetAttrs(iconifyGad, setWindow, NULL,
- GTCB_Checked, tempIconify,
- TAG_END);
- }
-
- break;
- }
- }
-