// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free // software: you can redistribute it and/or modify it under the terms of // the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection /* $Header: /CounterStrike/GADGET.CPP 1 3/03/97 10:24a Joe_bostic $ */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : Command & Conquer * * * * File Name : GADGET.CPP * * * * Programmer : Maria del Mar McCready Legg * * Joe L. Bostic * * * * Start Date : 01/03/95 * * * * Last Update : August 1, 1996 [JLB] * * * *---------------------------------------------------------------------------------------------* * Functions: * * GadgetClass::Action -- Base action for gadget. * * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * * GadgetClass::Delete_List -- Deletes all gadget objects in list. * * GadgetClass::Disable -- Disables the gadget from input processing. * * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * * GadgetClass::Enable -- Enables the gadget. * * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * * GadgetClass::GadgetClass -- Constructor for gadget object. * * GadgetClass::GadgetClass -- Default constructor for a gadget class object. * * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * * GadgetClass::Remove -- Removes the specified gadget from the list. * * GadgetClass::Set_Focus -- Sets focus to this gadget. * * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * * GadgetClass::~GadgetClass -- Destructor for gadget object. * * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * * GadgetClass::GadgetClass -- Constructor for the gadget object. * * GadgetClass::Set_Position -- Set the coordinate position of this gadget. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" /* ** This records the current gadget the the gadget system is "stuck on". Such a ** gadget will be processed to the exclusion of all others until the mouse button ** is no longer pressed. */ GadgetClass * GadgetClass::StuckOn = 0; /* ** This is a copy of a pointer to the last list used by the gadget input system. ** If a change of list is detected, then all gadgets are forced to be redrawn. */ GadgetClass * GadgetClass::LastList = 0; /* ** This points to the gadget that is intercepting all keyboard events. */ GadgetClass * GadgetClass::Focused = 0; /* ** This points to the current color scheme for drawing all gadgets. */ static RemapControlType _GreyScheme = {15}; RemapControlType * GadgetClass::ColorScheme = &_GreyScheme; /*********************************************************************************************** * GadgetClass::GadgetClass -- Constructor for gadget object. * * * * This is the normal constructor for gadget objects. A gadget object is only concerned * * with the region on the screen to considered "its own" as well as the flags that tell * * what mouse action should be recognized when the mouse is over this screen area. * * * * INPUT: x,y -- Coordinates (in pixels) of the upper left hand corner of the region that * * will be "owned" by this gadget. * * * * w,h -- Width and height (in pixels) of this gadget's region. * * * * flags -- The flags (mouse conditions) that will cause this gadget's action * * function to be called. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/03/1995 MML : Created. * *=============================================================================================*/ GadgetClass::GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky) : X(x), Y(y), Width(w), Height(h), IsToRepaint(false), IsSticky(sticky), IsDisabled(false), Flags(flags) { if (IsSticky) { Flags |= LEFTPRESS|LEFTRELEASE; } } /*********************************************************************************************** * GadgetClass::GadgetClass -- Constructor for the gadget object. * * * * This is the copy constructor for the gadget object. It will try to duplicate the * * righthand gadget. * * * * INPUT: gadget -- Reference to the initilization gadget. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 08/01/1996 JLB : Created. * *=============================================================================================*/ GadgetClass::GadgetClass(GadgetClass const & gadget) : X(gadget.X), Y(gadget.Y), Width(gadget.Width), Height(gadget.Height), IsToRepaint(gadget.IsToRepaint), IsSticky(gadget.IsSticky), IsDisabled(gadget.IsDisabled), Flags(gadget.Flags) { } /*********************************************************************************************** * GadgetClass::~GadgetClass -- Destructor for gadget object. * * * * This is the destructor for the gadget object. It will clear the focus from this gadget * * if this gadget currently has the focus. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/08/1995 JLB : Created. * *=============================================================================================*/ GadgetClass::~GadgetClass(void) { if (Has_Focus()) { Clear_Focus(); } if (this == StuckOn) { StuckOn = NULL; } if (this == LastList) { LastList = NULL; } if (this == Focused) { Focused = NULL; } } /*************************************************************************** * GADGETCLASS::CLICKEDON -- If a mouse click is detected within gadget's * * area and the appropriate flag is set, then call Action(). * * * * INPUT: int key, int mousex, int mousey * * * * OUTPUT: true or false * * * * WARNINGS: none. * * * * HISTORY: 01/03/1995 MML : Created. * *=========================================================================*/ int GadgetClass::Clicked_On(KeyNumType & key, unsigned flags, int mousex, int mousey) { /* ** Set flags to match only those events that occur AND are being looked for. If ** the result is NULL, then we know that this button should be ignored. */ flags &= Flags; /* ** If keyboard input should be processed by this "gadget" and keyboard input is ** detected, then always call the action function. It is up to the action function ** in this case to either ignore the keyboard input or not. ** ** For mouse actions, check to see if the mouse is in the region of the button ** before calling the associated action function. This is the typical action for ** buttons. */ if (this == StuckOn || (flags & KEYBOARD) || (flags && (mousex - X) < Width && (mousey - Y) < Height)) { return(Action(flags, key)); } return(false); } /*********************************************************************************************** * GadgetClass::Enable -- Enables the gadget. * * * * This function enables the gadget. An enabled gadget will be processed for input * * purposes. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ void GadgetClass::Enable(void) { IsDisabled = false; IsToRepaint = true; Clear_Focus(); } /*********************************************************************************************** * GadgetClass::Disable -- Disables the gadget from input processing. * * * * This routine will disable the gadget. A disabled gadget might be rendered, but is * * ignored for input processing. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ void GadgetClass::Disable(void) { IsDisabled = true; IsToRepaint = true; Clear_Focus(); } /*********************************************************************************************** * GadgetClass::Remove -- Removes the specified gadget from the list. * * * * Use this routine if an individual gadget needs to be removed from the list of gadgets. * * * * INPUT: none * * * * OUTPUT: bool; Was the specified gadget found and removed? A false indicates that the * * gadget wasn't in the list. * * * * WARNINGS: none * * * * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ GadgetClass * GadgetClass::Remove(void) { Clear_Focus(); return(GadgetClass *)LinkClass::Remove(); } /*********************************************************************************************** * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * * * * This returns with the next gadget's pointer. It is identical to the base Get_Next() * * function, but returns a pointer to a GadgetClass object. * * * * INPUT: none * * * * OUTPUT: Returns with a pointer to the next gadget in the list. * * * * WARNINGS: none * * * * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ GadgetClass * GadgetClass::Get_Next(void) const { return(GadgetClass*)LinkClass::Get_Next(); } /*********************************************************************************************** * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * * * * This routine will return the previous gadget in the list. It is identical to the base * * function Get_Prev, but returns a pointer to a GadgetClass object. * * * * INPUT: none * * * * OUTPUT: Returns with a pointer to the previous gadget in the list. * * * * WARNINGS: none * * * * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ GadgetClass * GadgetClass::Get_Prev(void) const { return(GadgetClass*)LinkClass::Get_Prev(); } /*********************************************************************************************** * GadgetClass::Delete_List -- Deletes all gadget objects in list. * * * * This function will delete all gadgets in the list. It is the counterpart to the * * Create_One_Of functions. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: Any references to these gadget become invalidated by this routine. * * * * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ void GadgetClass::Delete_List(void) { GadgetClass * g = this; /* ** Move to head of the list. */ while (g->Get_Prev()) { g = g->Get_Prev(); } /* ** First delete all the gadgets following the first one. The reason the first one ** is kept around is that sometimes deleting one gadget will result in related gadgets ** in the same list also being deleted. The first gadget will always contain the ** correct gadget pointer. */ while (g) { g->Clear_Focus(); GadgetClass * temp = g; g = g->Get_Next(); delete temp; } } /*********************************************************************************************** * GadgetClass::Action -- Base action for gadget. * * * * This handles the base level action that a gadget performs when a qualifying input event * * is detected. This sets the redraw flag and returns true (to stop further processing). * * If no qualifying input event was detected, but this routine was called anyway, then * * don't perform any action. The call to this routine, in that case, must have been forced * * for some other reason. * * * * INPUT: flag -- The input event bits that qualify for this gadget. A NULL indicates that * * no qualifying event occurred. * * * * OUTPUT: bool; Should further gadget list processing be aborted? * * * * WARNINGS: none * * * * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ int GadgetClass::Action(unsigned flags, KeyNumType &) { /* ** If any of the event flags are active, then this indicates that something probably ** has changed the gadget. Flag the gadget to be redrawn. Also, make sure that ** any sticky flags are cleared up. */ if (flags) { IsToRepaint = true; Sticky_Process(flags); return(true); } return(false); } /*********************************************************************************************** * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * * * * At this level, there is no actual rendering taking place with the call to Draw_Me, but * * the IsToRepaint flag must be cleared. Derived objects will call this routine and if it * * returns true, they will perform their custom rendering. * * * * INPUT: forced -- Is this redraw forced by outside circumstances? * * * * OUTPUT: bool; Should the gadget imagery be redrawn? * * * * WARNINGS: none * * * * HISTORY: * * 01/14/1995 JLB : Created. * *=============================================================================================*/ int GadgetClass::Draw_Me(int forced) { if (forced || IsToRepaint) { IsToRepaint = false; return(true); } return(false); } /*********************************************************************************************** * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * * * * Use this function to cause all gadget in the list to be redrawn regardless of the state * * of the IsToRepaint flag. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/03/1995 MML : Created. * *=============================================================================================*/ void GadgetClass::Draw_All(bool forced) { GadgetClass * gadget = this; while (gadget != NULL) { gadget->Draw_Me(forced); gadget = gadget->Get_Next(); } } /*************************************************************************** * GADGETCLASS::PROCESSLIST -- Check list for a mouse click within a gadget* * * * INPUT: none. * * * * OUTPUT: key pressed. * * * * WARNINGS: none. * * * * HISTORY: 01/03/1995 MML : Created. * *=========================================================================*/ KeyNumType GadgetClass::Input(void) { int mousex, mousey; KeyNumType key; unsigned flags; int forced = false; /* ** Record this list so that a forced redraw only occurs the FIRST time the ** gadget list is passed to this routine. */ if (LastList != this) { LastList = this; forced = true; StuckOn = NULL; Focused = NULL; } /* ** Fetch any pending keyboard input. */ key = Keyboard->Check(); if (key != 0) { key = Keyboard->Get(); } #ifdef WIN32 #ifdef CHEAT_KEYS if (key == KN_K && !Debug_Map && (Debug_Flag || Debug_Playtest)) { /* ** time to create a screen shot using the PCX code (if it works) */ if (!Debug_MotionCapture) { GraphicBufferClass temp_page( SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL, SeenBuff.Get_Width() * SeenBuff.Get_Height()); CDFileClass file; char filename[30]; // Hide_Mouse(); SeenBuff.Blit(temp_page); // Show_Mouse(); for (int lp = 0; lp < 99; lp ++) { sprintf(filename, "scrsht%02d.pcx", lp); file.Set_Name(filename); if (!file.Is_Available()) break; } file.Cache(200000); Write_PCX_File(file, temp_page, & GamePalette); Sound_Effect(VOC_BEEP); } } #endif #endif /* ** For mouse button clicks, the mouse position is actually held in the MouseQ... ** globals rather than their normal Mouse... globals. This is because we need to ** know the position of the mouse at the exact instant when the click occurred ** rather the the mouse position at the time we get around to this function. */ if (((key&0xFF) == KN_LMOUSE) || ((key&0xFF) == KN_RMOUSE)) { mousex = Keyboard->MouseQX; mousey = Keyboard->MouseQY; } else { mousex = Get_Mouse_X(); mousey = Get_Mouse_Y(); } /* ** Set the mouse button state flags. These will be passed to the individual ** buttons so that they can determine what action to perform (if any). */ flags = 0; if (key) { if (key == KN_LMOUSE) { flags |= LEFTPRESS; } if (key == KN_RMOUSE) { flags |= RIGHTPRESS; } if (key == (KN_LMOUSE | KN_RLSE_BIT)) { flags |= LEFTRELEASE; } if (key == (KN_RMOUSE | KN_RLSE_BIT)) { flags |= RIGHTRELEASE; } } /* ** If the mouse wasn't responsible for this key code, then it must be from ** the keyboard. Flag this fact. */ if (key && !flags) { flags |= KEYBOARD; } /* ** Mouse button up or down action is ignored if there is a keyboard event. This ** allows keyboard events to fall through normally even if the mouse is over a ** gadget that is flagged for LEFTUP or RIGHTUP. */ if (!key) { /* ** Check for the mouse being held down. We can't use the normal input system ** for this, so we must examine the actual current state of the mouse ** buttons. As a side note, if we determine that the mouse button isn't being ** held down, then we automatically know that it must be up -- set the flag ** accordingly. */ if (Keyboard->Down(KN_LMOUSE)) { flags |= LEFTHELD; } else { flags |= LEFTUP; } if (Keyboard->Down(KN_RMOUSE)) { flags |= RIGHTHELD; } else { flags |= RIGHTUP; } } /* ** If "sticky" processing is active, then only process the stuck gadget. */ if (StuckOn) { StuckOn->Draw_Me(false); GadgetClass * oldstuck = StuckOn; StuckOn->Clicked_On(key, flags, mousex, mousey); if (StuckOn) { StuckOn->Draw_Me(false); } else { oldstuck->Draw_Me(false); } } else { /* ** If there is a gadget that has the keyboard focus, then route all keyboard ** events to it. */ if (Focused && (flags & KEYBOARD)) { Focused->Draw_Me(false); Focused->Clicked_On(key, flags, mousex, mousey); if (Focused) { Focused->Draw_Me(false); } } else { /* ** Sweep through all the buttons in the chain and pass the current button state ** and keyboard input data to them. These routines will detect whether they should ** perform some action and return a flag to this effect. They also have the option ** of changing the key value so that an appropriate return value is use for this ** processing routine. */ GadgetClass * next_button = this; while (next_button != NULL) { /* ** Maybe redraw the button if it needs to or is being forced to redraw. */ next_button->Draw_Me(forced); if (!next_button->IsDisabled) { /* ** Process this button. If the button was recognized and action was ** performed, then bail from further processing (speed reasons?). */ if (next_button->Clicked_On(key, flags, mousex, mousey)) { /* ** Some buttons will require repainting when they perform some action. ** Do so at this time. */ next_button->Draw_Me(false); break; } } next_button = next_button->Get_Next(); } } } return(key); } /*********************************************************************************************** * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * * * * This examines the gadget list looking for on that has the same ID as specified. If that * * gadget was found, then a pointer to it is returned. Since only ControlClass gadgets * * or ones derived from it can have an ID value, we know that the returned pointer is at * * least of the ControlClass type. * * * * INPUT: id -- The ID number to scan for. Zero is not a legal ID number and if passed in, * * a NULL will always be returned. * * * * OUTPUT: Returns with a pointer to the ControlClass gadget that has the matching ID number. * * If no matching gadget was found, then NULL is returned. * * * * WARNINGS: If there happens to be more than one gadget with a matching ID, this routine * * will return a pointer to the first one only. * * * * HISTORY: * * 01/16/1995 JLB : Created. * *=============================================================================================*/ ControlClass * GadgetClass::Extract_Gadget(unsigned id) { GadgetClass * g = this; if (id != 0) { while (g != NULL) { if (g->Get_ID() == id) { return((ControlClass *)g); } g = g->Get_Next(); } } return(0); } /*********************************************************************************************** * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * * * * Use this routine to flag the gadget to be redrawn. A gadget so flagged will have its * * Draw_Me function called at the next available opportunity. Usually, this is the next * * time the Input() function is called. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/16/1995 JLB : Created. * *=============================================================================================*/ void GadgetClass::Flag_To_Redraw(void) { IsToRepaint = true; } /*********************************************************************************************** * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * * * * This function examines the event flags and handles any "sticky" processing required. * * Sticky processing is when the button is flagged with the "IsSticky" bit and it will * * be processed to the exclusion of all other gadgets while the mouse button is held * * down. * * * * INPUT: flags -- The event flags that triggered the call to this routine. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/16/1995 JLB : Created. * *=============================================================================================*/ void GadgetClass::Sticky_Process(unsigned flags) { if (IsSticky && (flags & LEFTPRESS)) { StuckOn = this; } if (StuckOn == this && (flags & LEFTRELEASE)) { StuckOn = 0; } } /*********************************************************************************************** * GadgetClass::Set_Focus -- Sets focus to this gadget. * * * * This will set the focus to this gadget regardless of any current focus setting. If there * * is another gadget that has focus, it will have its focus cleared before this gadget will * * get the focus. A focused gadget is one that has all keyboard input routed to it. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/25/1995 JLB : Created. * *=============================================================================================*/ void GadgetClass::Set_Focus(void) { if (Focused) { Focused->Flag_To_Redraw(); Focused->Clear_Focus(); } Flags |= KEYBOARD; Focused = this; } /*********************************************************************************************** * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * * * * Use this function to clear the focus for the gadget. If the gadget doesn't currently * * have focus, then this routine will do nothing. For added functionality, overload this * * virtual function so that gadget specific actions may be take when focus is lost. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/25/1995 JLB : Created. * *=============================================================================================*/ void GadgetClass::Clear_Focus(void) { if (Focused == this) { Flags &= ~KEYBOARD; Focused = 0; } } /*********************************************************************************************** * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * * * * If this object has the keyboard focus, then this routine will return true. When the * * gadget has keyboard focus, all keyboard events get routed to the gadget. * * * * INPUT: none * * * * OUTPUT: bool; Does this gadget have the keyboard focus? * * * * WARNINGS: none * * * * HISTORY: * * 01/21/1995 JLB : Created. * *=============================================================================================*/ bool GadgetClass::Has_Focus(void) { return(this == Focused); } /*********************************************************************************************** * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * * * * This function is mostly for supporting HidPage drawing. If it returns true, it means * * the application needs to re-blit the HidPage forward, after calling the list's Input(). * * * * INPUT: none * * * * OUTPUT: true = an item needs redrawing, false = no items need redrawing * * * * WARNINGS: It is assumed 'this' is the head of the list. * * * * HISTORY: * * 01/03/1995 MML : Created. * *=============================================================================================*/ int GadgetClass::Is_List_To_Redraw(void) { GadgetClass * gadget = this; while (gadget != NULL) { if (gadget->IsToRepaint) { return (true); } gadget = gadget->Get_Next(); } return (false); } /*********************************************************************************************** * GadgetClass::Set_Position -- Set the coordinate position of this gadget. * * * * This routine helps with moving a gadget's location. It will set the gadgets upper * * left corner to the pixel location specified. * * * * INPUT: x,y -- The X and Y position to put the gadget's upper left corner to. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 08/01/1996 JLB : Created. * *=============================================================================================*/ void GadgetClass::Set_Position(int x, int y) { X = x; Y = y; }