gcsx_frame.cpp 125 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850
  1. /* GCSx
  2. ** FRAME.CPP
  3. **
  4. ** Frame windows
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. // @TODO: several [pos] loops that refer to clientArea could be iterator-based now
  24. #include "all.h"
  25. #define DEBUG_INFO_CLIENTS \
  26. debugWrite("Clients %s: (0-%d)", debugDump(), clientArea.size() - 1); \
  27. for (int debug_pos = 0; debug_pos < (int)clientArea.size(); ++debug_pos) { \
  28. if (clientArea[debug_pos].window) { \
  29. debugWrite(" %d: %d,%d (%d x %d, show %d x %d) %s", debug_pos, \
  30. clientArea[debug_pos].x, clientArea[debug_pos].y, \
  31. clientArea[debug_pos].width, clientArea[debug_pos].height, \
  32. clientArea[debug_pos].widthShown, clientArea[debug_pos].heightShown, \
  33. clientArea[debug_pos].window->debugDump()); \
  34. } \
  35. }
  36. int FrameWindow::cascadeStep = -1;
  37. int FrameWindow::minimumPanelSize = 0;
  38. const char* FrameWindow::wtButtonAll = "hgf";
  39. const char* FrameWindow::wtButtonNoResize = "hf";
  40. const string FrameWindow::wtButtonClose("f");
  41. const string FrameWindow::wtButtonUp("m");
  42. const string FrameWindow::wtButtonDown("n");
  43. const string FrameWindow::wtButtonLeft("l");
  44. const string FrameWindow::wtButtonRight("k");
  45. FrameWindow::FrameWindow(const string& title, ResizableType isResizable, FrameType type, Window* client, TitlebarType hasTitlebarType, int hasOuterBorder, FrameWindow* isFramePanel) : Window(), titlebar(title), clientArea() { start_func
  46. assert(type != FRAMETYPE_NOCHANGE);
  47. assert(hasTitlebarType != TITLEBAR_NOCHANGE);
  48. assert(isResizable != RESIZING_NOCHANGE);
  49. assert(hasOuterBorder != -1);
  50. minimumPanelSize = fontHeight(FONT_TOOLTITLEBAR) + FRAME_TOOLTITLETOPPAD + FRAME_TOOLTITLEBOTTOMPAD;
  51. // This is the first place we might error- so far, nothing needs undoing until client is pushed
  52. ClientArea ca = { client, -1, -1, -1, -1, -1, -1, CLIENT_PRIMARY };
  53. clientArea.push_back(ca);
  54. state = FRAME_STATE_NORMAL;
  55. dragMode = FRAME_DRAG_NONE;
  56. deleteMe = 1;
  57. haveFocus = subFocus = 0;
  58. scrollX = scrollY = 0;
  59. scrollRepeatTicks = scrollRepeatDelay = 0;
  60. hScrollLine = vScrollLine = fontHeight();
  61. whyDirty = FRAME_DIRTY_ALL;
  62. floatTarget = 0;
  63. autoCenterScroll = 0;
  64. reserve[0] = reserve[1] = reserve[2] = reserve[3] = 0;
  65. // (we set outerBorder only because setFeatures looks at it)
  66. outerBorder = 0;
  67. framePanel = NULL;
  68. setFeatures(isResizable, type, hasTitlebarType, hasOuterBorder, isFramePanel, 1);
  69. // Initial size
  70. if (client) {
  71. client->setParent(this);
  72. width = client->getWidth();
  73. if (width < titlebarButtonTotalWidth) width = titlebarButtonTotalWidth;
  74. height = client->getHeight();
  75. if (height < 0) height = 0;
  76. width += outerBorder * 2 + FRAME_INNERBORDER * 2;
  77. height += outerBorder * 2 + FRAME_INNERBORDER * 2 + titlebarHeight;
  78. }
  79. else {
  80. width = outerBorder * 2 + 2 + titlebarButtonTotalWidth;
  81. height = titlebarHeight + 2 * outerBorder;
  82. }
  83. }
  84. void FrameWindow::setFeatures(ResizableType isResizable, FrameType type, TitlebarType hasTitlebarType, int hasOuterBorder, FrameWindow* isFramePanel, int skipResizing) { start_func
  85. int oldOuterBorder = outerBorder;
  86. if (hasTitlebarType != TITLEBAR_NOCHANGE) titlebarType = hasTitlebarType;
  87. if (isFramePanel != NULL) framePanel = isFramePanel;
  88. if (hasOuterBorder != -1) outerBorder = hasOuterBorder ? FRAME_OUTERBORDER : 0;
  89. if (isResizable != RESIZING_NOCHANGE) resizable = isResizable;
  90. if (type != FRAMETYPE_NOCHANGE) frameType = type;
  91. // Recalculate our constants- small chance of VideoException in this section
  92. titlebarX = outerBorder + FRAME_INNERBORDER - FRAME_BEVELTHICKNESS;
  93. titlebarY = outerBorder;
  94. titlebarHeight = (titlebarType == TITLEBAR_TOOL ? FRAME_TOOLTITLETOPPAD + FRAME_TOOLTITLEBOTTOMPAD : FRAME_TITLETOPPAD + FRAME_TITLEBOTTOMPAD) + fontHeight(titlebarType == TITLEBAR_TOOL ? FONT_TOOLTITLEBAR : FONT_TITLEBAR);
  95. titlebarButtonY = titlebarY + 1;
  96. titlebarButtonSize = titlebarHeight - 2;
  97. titlebarButtonXPad = (titlebarButtonSize - fontWidth(wtButtonClose, titlebarType == TITLEBAR_TOOL ? FONT_TOOLTITLEBARWIDGET : FONT_TITLEBARWIDGET)) / 2;
  98. scrollbarButtonSize = fontHeight(FONT_WIDGET) + 2;
  99. scrollbarButtonXPad = (scrollbarButtonSize - fontWidth(wtButtonClose, FONT_WIDGET)) / 2;
  100. if (titlebarType == TITLEBAR_OFF) {
  101. titlebarHeight = 0;
  102. titlebarButtons[0] = 0;
  103. }
  104. else if ((resizable) && (!framePanel)) {
  105. strcpy(titlebarButtons, wtButtonAll);
  106. }
  107. else {
  108. strcpy(titlebarButtons, wtButtonNoResize);
  109. }
  110. if (state == FRAME_STATE_MINIMIZED) {
  111. char* button = strchr(titlebarButtons, 'h');
  112. if (button) *button = 'j';
  113. }
  114. if (state == FRAME_STATE_MAXIMIZED) {
  115. char* button = strchr(titlebarButtons, 'g');
  116. if (button) *button = 'j';
  117. }
  118. titlebarButtonTotalWidth = titlebarButtonSize * strlen(titlebarButtons);
  119. // Resize to fit new outer border and minimum titlebar width
  120. if (!skipResizing) {
  121. int newWidth = width + outerBorder * 2 - oldOuterBorder * 2;
  122. int newHeight = height + outerBorder * 2 - oldOuterBorder * 2;
  123. if (newWidth < outerBorder * 2 + 2 + titlebarButtonTotalWidth) newWidth = outerBorder * 2 + 2 + titlebarButtonTotalWidth;
  124. int newX = x;
  125. int newY = y;
  126. confineSize(newX, newY, newWidth, newHeight);
  127. move(newX, newY);
  128. resize(newWidth, newHeight);
  129. }
  130. }
  131. FrameWindow* FrameWindow::getFramePanel() { start_func
  132. return framePanel;
  133. }
  134. void FrameWindow::requestViewSize(int requestWidth, int requestHeight) { start_func
  135. if (state != FRAME_STATE_MAXIMIZED) {
  136. requestWidth += width - clientArea[0].width;
  137. requestHeight += height - clientArea[0].height;
  138. if (width > requestWidth) requestWidth = width;
  139. if (height > requestHeight) requestHeight = height;
  140. int newX = x;
  141. int newY = y;
  142. confineSize(newX, newY, requestWidth, requestHeight);
  143. move(newX, newY);
  144. resize(requestWidth, requestHeight);
  145. }
  146. }
  147. void FrameWindow::dropClients() { start_func
  148. vector<ClientArea>::iterator end = clientArea.end();
  149. for (vector<ClientArea>::iterator pos = clientArea.begin(); pos != end; ++pos) {
  150. if ((*pos).window) {
  151. (*pos).window->setParent(NULL);
  152. (*pos).window = NULL;
  153. }
  154. }
  155. }
  156. // Determines size of reserves based on existing window size, then relocates
  157. // all tool windows and central client window
  158. void FrameWindow::reorganizeReserves() { start_func
  159. int max = clientArea.size();
  160. int countPanel[4];
  161. // Determine max sizes of all items
  162. for (int spot = CLIENT_RIGHT; spot <= CLIENT_BOTTOM; ++spot) {
  163. reserve[spot] = 0;
  164. countPanel[spot] = 0;
  165. for (int pos = 1; pos < max; ++pos) {
  166. if (clientArea[pos].window) {
  167. // Pull size from window itself?
  168. if (clientArea[pos].width < 0) clientArea[pos].width = clientArea[pos].window->getWidth();
  169. if (clientArea[pos].height < 0) clientArea[pos].height = clientArea[pos].window->getHeight();
  170. // (while we're here, unhide everything for now)
  171. clientArea[pos].position = clientArea[pos].position & ~CLIENT_HIDDEN;
  172. if (clientArea[pos].position == spot) {
  173. ++countPanel[spot];
  174. if (spot & CLIENT_TOPBOTTOM) {
  175. if (clientArea[pos].height > reserve[spot]) {
  176. reserve[spot] = clientArea[pos].height;
  177. }
  178. }
  179. else {
  180. if (clientArea[pos].width > reserve[spot]) {
  181. reserve[spot] = clientArea[pos].width;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. // Add in gutter if needed
  188. if (reserve[spot]) {
  189. reserve[spot] += FRAME_CLIENTPAD;
  190. }
  191. }
  192. // Start with entire area dedicated to client area INCLUDING inner border
  193. int mainWidth = width - (outerBorder * 2);
  194. int mainHeight = height - (outerBorder * 2 + titlebarHeight);
  195. int mainX = outerBorder;
  196. int mainY = titlebarY + titlebarHeight;
  197. // Add in each area that exists, as long as client area has space to "give"
  198. // If not, "hide" area
  199. // Add in top?
  200. if ((reserve[CLIENT_TOP]) && (mainWidth > 0) && (mainHeight > 0)) {
  201. if (mainHeight < reserve[CLIENT_TOP]) {
  202. reserve[CLIENT_TOP] = mainHeight;
  203. }
  204. mainY += reserve[CLIENT_TOP];
  205. mainHeight -= reserve[CLIENT_TOP];
  206. reserve[CLIENT_TOP] -= FRAME_CLIENTPAD;
  207. }
  208. else {
  209. reserve[CLIENT_TOP] = 0;
  210. }
  211. // Bottom?
  212. if ((reserve[CLIENT_BOTTOM]) && (mainWidth > 0) && (mainHeight > 0)) {
  213. if (mainHeight < reserve[CLIENT_BOTTOM]) {
  214. reserve[CLIENT_BOTTOM] = mainHeight;
  215. }
  216. mainHeight -= reserve[CLIENT_BOTTOM];
  217. reserve[CLIENT_BOTTOM] -= FRAME_CLIENTPAD;
  218. }
  219. else {
  220. reserve[CLIENT_BOTTOM] = 0;
  221. }
  222. // Left?
  223. if ((reserve[CLIENT_LEFT]) && (mainWidth > 0) && (mainHeight > 0)) {
  224. if (mainWidth < reserve[CLIENT_LEFT]) {
  225. reserve[CLIENT_LEFT] = mainWidth;
  226. }
  227. mainX += reserve[CLIENT_LEFT];
  228. mainWidth -= reserve[CLIENT_LEFT];
  229. reserve[CLIENT_LEFT] -= FRAME_CLIENTPAD;
  230. }
  231. else {
  232. reserve[CLIENT_LEFT] = 0;
  233. }
  234. // Right?
  235. if ((reserve[CLIENT_RIGHT]) && (mainWidth > 0) && (mainHeight > 0)) {
  236. if (mainWidth < reserve[CLIENT_RIGHT]) {
  237. reserve[CLIENT_RIGHT] = mainWidth;
  238. }
  239. mainWidth -= reserve[CLIENT_RIGHT];
  240. reserve[CLIENT_RIGHT] -= FRAME_CLIENTPAD;
  241. }
  242. else {
  243. reserve[CLIENT_RIGHT] = 0;
  244. }
  245. // Loop through all areas again, placing in proper positions or hiding as needed
  246. for (int spot = CLIENT_RIGHT; spot <= CLIENT_BOTTOM; ++spot) {
  247. int nextCoord = 0;
  248. int lastDid = 0;
  249. // Minimum size for all panels combined (reduces as we proceed)
  250. int minSizeForAll = (minimumPanelSize + FRAME_CLIENTPAD) * countPanel[spot];
  251. for (int pos = 1; pos < max; ++pos) {
  252. if ((clientArea[pos].position == spot) && (clientArea[pos].window)) {
  253. // Hiding entire side?
  254. if (reserve[spot] <= 0) {
  255. clientArea[pos].position |= CLIENT_HIDDEN;
  256. }
  257. else {
  258. // Account for our share of the minimum size
  259. minSizeForAll -= minimumPanelSize + FRAME_CLIENTPAD;
  260. // Assume we get entire desired area for now
  261. clientArea[pos].widthShown = clientArea[pos].width;
  262. clientArea[pos].heightShown = clientArea[pos].height;
  263. // Place in next position
  264. if (spot & CLIENT_TOPBOTTOM) {
  265. // Horizontal position
  266. clientArea[pos].x = nextCoord + outerBorder;
  267. // Clip width? (accounting for min size of future panels)
  268. if (clientArea[pos].x + clientArea[pos].widthShown > width - outerBorder - minSizeForAll) {
  269. clientArea[pos].widthShown = width - outerBorder - clientArea[pos].x - minSizeForAll;
  270. }
  271. // Minimum size
  272. if (clientArea[pos].widthShown < minimumPanelSize) {
  273. clientArea[pos].widthShown = minimumPanelSize;
  274. }
  275. // Clip width? (if minimum size is too much, not accounting for future panels)
  276. if (clientArea[pos].x + clientArea[pos].widthShown > width - outerBorder) {
  277. clientArea[pos].widthShown = width - outerBorder - clientArea[pos].x;
  278. }
  279. // Hidden?
  280. if (clientArea[pos].widthShown <= 0) {
  281. clientArea[pos].position |= CLIENT_HIDDEN;
  282. }
  283. // Next position
  284. nextCoord += clientArea[pos].widthShown + FRAME_CLIENTPAD;
  285. // Height is elementary
  286. clientArea[pos].heightShown = reserve[spot];
  287. // Vertical position is elementary
  288. if (spot == CLIENT_TOP) {
  289. clientArea[pos].y = titlebarY + titlebarHeight;
  290. }
  291. else {
  292. clientArea[pos].y = mainY + mainHeight + FRAME_CLIENTPAD;
  293. }
  294. }
  295. else {
  296. // Vertical position
  297. clientArea[pos].y = nextCoord + titlebarY + titlebarHeight + reserve[CLIENT_TOP];
  298. // Clip height? (accounting for min size of future panels)
  299. if (clientArea[pos].y + clientArea[pos].heightShown > mainY + mainHeight - minSizeForAll) {
  300. clientArea[pos].heightShown = mainY + mainHeight - clientArea[pos].y - minSizeForAll;
  301. }
  302. // Minimum size
  303. if (clientArea[pos].heightShown < minimumPanelSize) {
  304. clientArea[pos].heightShown = minimumPanelSize;
  305. }
  306. // Clip height? (if minimum size is too much, not accounting for future panels)
  307. if (clientArea[pos].y + clientArea[pos].heightShown > mainY + mainHeight) {
  308. clientArea[pos].heightShown = mainY + mainHeight - clientArea[pos].y;
  309. }
  310. // Hidden?
  311. if (clientArea[pos].heightShown <= 0) {
  312. clientArea[pos].position |= CLIENT_HIDDEN;
  313. }
  314. // Next position
  315. nextCoord += clientArea[pos].heightShown + FRAME_CLIENTPAD;
  316. // Width is elementary
  317. clientArea[pos].widthShown = reserve[spot];
  318. // Horizontal position is elementary
  319. if (spot == CLIENT_LEFT) {
  320. clientArea[pos].x = outerBorder;
  321. }
  322. else {
  323. clientArea[pos].x = mainX + mainWidth + FRAME_CLIENTPAD;
  324. }
  325. }
  326. lastDid = pos;
  327. }
  328. if (clientArea[pos].position & CLIENT_HIDDEN) {
  329. clientArea[pos].widthShown = 0;
  330. clientArea[pos].heightShown = 0;
  331. }
  332. }
  333. }
  334. // We now resize the last one, if it wasn't hidden- if there's more space
  335. if ((lastDid) && (!(clientArea[lastDid].position & CLIENT_HIDDEN))) {
  336. if (spot & CLIENT_TOPBOTTOM) {
  337. // Extend width?
  338. if (clientArea[lastDid].x + clientArea[lastDid].widthShown < width - outerBorder) {
  339. clientArea[lastDid].widthShown = width - outerBorder - clientArea[lastDid].x;
  340. }
  341. }
  342. else {
  343. // Extend height?
  344. if (clientArea[lastDid].y + clientArea[lastDid].heightShown < mainY + mainHeight) {
  345. clientArea[lastDid].heightShown = mainY + mainHeight - clientArea[lastDid].y;
  346. }
  347. }
  348. }
  349. }
  350. // Client area now loses space for inner borders
  351. mainWidth -= FRAME_INNERBORDER * 2;
  352. mainHeight -= FRAME_INNERBORDER * 2;
  353. if (mainWidth < 0) mainWidth = 0;
  354. if (mainHeight < 0) mainHeight = 0;
  355. // Finally, we apply the calculations to the main client area
  356. clientArea[0].x = mainX + FRAME_INNERBORDER;
  357. clientArea[0].y = mainY + FRAME_INNERBORDER;
  358. clientArea[0].width = mainWidth;
  359. clientArea[0].height = mainHeight;
  360. clientArea[0].widthShown = mainWidth;
  361. clientArea[0].heightShown = mainHeight;
  362. }
  363. int FrameWindow::findToolPanel(Window* toolPanel) { start_func
  364. assert(toolPanel);
  365. for (int pos = 1; pos < (int)clientArea.size(); ++pos) {
  366. if (clientArea[pos].window == toolPanel) return pos;
  367. }
  368. return 0;
  369. }
  370. void FrameWindow::addToolPanel(FrameWindow* toolPanel, int position, vector<ClientArea>::iterator* insertBefore) { start_func
  371. assert(toolPanel);
  372. position &= ~CLIENT_HIDDEN;
  373. int newHeight = height;
  374. int newWidth = width;
  375. int newX = x;
  376. int newY = y;
  377. int desiredSize = -1;
  378. int wasMinimized = toolPanel->isMinimized();
  379. if (wasMinimized) toolPanel->restore();
  380. // Do this now so panel size is as it will be (border and all)
  381. // Add as managed (temporarily ensure not notifying anyone, to eliminate redundant notifications)
  382. toolPanel->setParentNotify(NULL);
  383. toolPanel->setParent(NULL);
  384. if (position == CLIENT_FLOAT) { // (but not closed)
  385. toolPanel->show(SHOW_CURRENT, SHOW_CURRENT, SHOW_CURRENT, SHOW_CURRENT);
  386. toolPanel->setFeatures(RESIZING_NOCHANGE, FRAMETYPE_NOCHANGE, TITLEBAR_NOCHANGE, 1, this);
  387. }
  388. else {
  389. toolPanel->hide();
  390. toolPanel->setFeatures(RESIZING_NOCHANGE, FRAMETYPE_NOCHANGE, TITLEBAR_NOCHANGE, 0, this);
  391. toolPanel->setParent(this);
  392. }
  393. toolPanel->setParentNotify(this);
  394. // Rearrange panels to make room?
  395. if (!(position & CLIENT_NOTMANAGED)) {
  396. // Count panels on this side and note the last one
  397. int count = 0;
  398. int last = 0;
  399. int pos;
  400. for (pos = 1; pos < (int)clientArea.size(); ++pos) {
  401. if ((clientArea[pos].window) && (!(clientArea[pos].position & CLIENT_NOTMANAGED)) && ((clientArea[pos].position & CLIENT_GETSIDE) == (position & CLIENT_GETSIDE))) {
  402. count++;
  403. last = pos;
  404. }
  405. }
  406. // Assuming we're not the only panel on this size, we need to manage the size
  407. // If we are, we'll just end up resizing at worst
  408. int totalSize;
  409. int prevSize = 0;
  410. // Desired size is the lesser of requested size or our fair share of the panel
  411. // Also calculate the total size of all previous items on this panel
  412. if (position & CLIENT_TOPBOTTOM) {
  413. desiredSize = toolPanel->getWidth();
  414. totalSize = width - (outerBorder * 2);
  415. if (count) {
  416. // Base off of requested size- not actual
  417. prevSize = clientArea[last].x - outerBorder + clientArea[last].width;
  418. }
  419. }
  420. else {
  421. desiredSize = toolPanel->getHeight();
  422. totalSize = height - titlebarY - outerBorder - titlebarHeight - reserve[CLIENT_TOP] - reserve[CLIENT_BOTTOM];
  423. if (count) {
  424. prevSize = clientArea[last].y - titlebarY - titlebarHeight - reserve[CLIENT_TOP] + clientArea[last].height;
  425. }
  426. }
  427. // Apply any empty space to our "requirement"
  428. desiredSize -= totalSize - prevSize;
  429. // If more room needed, we can try resizing
  430. if (desiredSize > 0) {
  431. if (position & CLIENT_TOPBOTTOM) {
  432. int oldSize = newWidth;
  433. newWidth += desiredSize;
  434. confineSize(newX, newY, newWidth, newHeight);
  435. desiredSize -= newWidth - oldSize;
  436. }
  437. else {
  438. int oldSize = newHeight;
  439. newHeight += desiredSize;
  440. confineSize(newX, newY, newWidth, newHeight);
  441. desiredSize -= newHeight - oldSize;
  442. }
  443. }
  444. // Limit any remaining desired size to our fair share of the previous space
  445. if (desiredSize > totalSize / (count + 1)) {
  446. desiredSize = totalSize / (count + 1);
  447. }
  448. // Is more room still needed?
  449. if ((desiredSize > 0) && (count)) {
  450. int totalDesiredSize = desiredSize;
  451. // Rearrange all other clients equally to make room
  452. for (pos = 1; pos < (int)clientArea.size(); ++pos) {
  453. if ((clientArea[pos].window) && (!(clientArea[pos].position & CLIENT_NOTMANAGED)) && ((clientArea[pos].position & CLIENT_GETSIDE) == (position & CLIENT_GETSIDE))) {
  454. if (position & CLIENT_TOPBOTTOM) {
  455. // Decrease size by an amount based on ratio of total size
  456. clientArea[pos].width -= totalDesiredSize * clientArea[pos].width / totalSize;
  457. desiredSize -= totalDesiredSize * clientArea[pos].width / totalSize;
  458. }
  459. else {
  460. // Decrease size by an amount based on ratio of total size
  461. clientArea[pos].height -= totalDesiredSize * clientArea[pos].height / totalSize;
  462. desiredSize -= totalDesiredSize * clientArea[pos].height / totalSize;
  463. }
  464. }
  465. }
  466. }
  467. // If any "desired size" left, deduct it from what's allowed
  468. if (position & CLIENT_TOPBOTTOM) {
  469. desiredSize = toolPanel->getWidth() - ((desiredSize > 0) ? desiredSize : 0);
  470. }
  471. else {
  472. desiredSize = toolPanel->getHeight() - ((desiredSize > 0) ? desiredSize : 0);
  473. }
  474. // Resize window if new panel is docked and larger than the reserve already there
  475. if (state != FRAME_STATE_MAXIMIZED) {
  476. if ((position & CLIENT_GETSIDE) == CLIENT_LEFT) {
  477. if (toolPanel->getWidth() > reserve[CLIENT_LEFT]) {
  478. newWidth += toolPanel->getWidth() - reserve[CLIENT_LEFT];
  479. if (reserve[CLIENT_LEFT] == 0) newWidth += FRAME_CLIENTPAD;
  480. }
  481. }
  482. if ((position & CLIENT_GETSIDE) == CLIENT_RIGHT) {
  483. if (toolPanel->getWidth() > reserve[CLIENT_RIGHT]) {
  484. newWidth += toolPanel->getWidth() - reserve[CLIENT_RIGHT];
  485. if (reserve[CLIENT_LEFT] == 0) newWidth += FRAME_CLIENTPAD;
  486. }
  487. }
  488. if ((position & CLIENT_GETSIDE) == CLIENT_TOP) {
  489. if (toolPanel->getHeight() > reserve[CLIENT_TOP]) {
  490. newHeight += toolPanel->getHeight() - reserve[CLIENT_TOP];
  491. if (reserve[CLIENT_LEFT] == 0) newWidth += FRAME_CLIENTPAD;
  492. }
  493. }
  494. if ((position & CLIENT_GETSIDE) == CLIENT_BOTTOM) {
  495. if (toolPanel->getHeight() > reserve[CLIENT_BOTTOM]) {
  496. newHeight += toolPanel->getHeight() - reserve[CLIENT_BOTTOM];
  497. if (reserve[CLIENT_LEFT] == 0) newWidth += FRAME_CLIENTPAD;
  498. }
  499. }
  500. confineSize(newX, newY, newWidth, newHeight);
  501. move(newX, newY);
  502. }
  503. }
  504. // Add to panels
  505. ClientArea ca = { toolPanel, -1, -1,
  506. (position & CLIENT_TOPBOTTOM) ? desiredSize : -1,
  507. (position & CLIENT_TOPBOTTOM) ? -1 : desiredSize,
  508. -1, -1, position };
  509. if ((insertBefore) && ((*insertBefore) != clientArea.end())) {
  510. clientArea.insert(*insertBefore, ca);
  511. }
  512. else {
  513. clientArea.push_back(ca);
  514. }
  515. // Handles resize and also rearranges client areas
  516. resize(newWidth, newHeight);
  517. if (wasMinimized) toolPanel->minimize();
  518. return;
  519. }
  520. void FrameWindow::removeToolPanel(Window* toolPanel) { start_func
  521. assert(toolPanel);
  522. assert(clientArea[0].window != toolPanel);
  523. vector<ClientArea>::iterator end = clientArea.end();
  524. for (vector<ClientArea>::iterator pos = clientArea.begin(); pos != end; ++pos) {
  525. if ((*pos).window == toolPanel) {
  526. (*pos).window->setParent(NULL);
  527. clientArea.erase(pos);
  528. break;
  529. }
  530. }
  531. // @TODO: Do we need to check if toolpanel is referenced in a drag setting, subfocus, etc?
  532. // Rearranges client areas as needed
  533. resize(width, height);
  534. }
  535. void FrameWindow::setTitle(const string& newTitle) { start_func
  536. // (this line could memfault)
  537. titlebar = newTitle;
  538. setDirty();
  539. whyDirty |= FRAME_DIRTY_TITLE;
  540. }
  541. Window::WindowType FrameWindow::windowType() const { start_func
  542. return WINDOW_FRAME;
  543. }
  544. #ifndef NDEBUG
  545. const char* FrameWindow::debugDump() const { start_func
  546. return titlebar.c_str();
  547. }
  548. #endif
  549. void FrameWindow::nextWindow() { start_func
  550. // Cancel if modal or not on desktop
  551. if ((windowSort() == WINDOWSORT_MODAL) || (parent != desktop)) return;
  552. desktop->bringToTop(desktop->findWindow(WINDOW_FRAME, 0));
  553. }
  554. void FrameWindow::prevWindow() { start_func
  555. // Cancel if modal or not on desktop
  556. if ((windowSort() == WINDOWSORT_MODAL) || (parent != desktop)) return;
  557. // Find us
  558. int pos = 0;
  559. while (desktop->findWindow(WINDOW_FRAME, pos) != this) {
  560. // (Shouldn't be NULL unless we aren't in collection!)
  561. assert(desktop->findWindow(WINDOW_FRAME, pos));
  562. ++pos;
  563. }
  564. if (--pos >= 0) {
  565. desktop->bringToTop(desktop->findWindow(WINDOW_FRAME, pos));
  566. desktop->sendToBottom(this);
  567. }
  568. }
  569. int FrameWindow::isMinimized() { start_func
  570. return (state == FRAME_STATE_MINIMIZED) ? 1 : 0;
  571. }
  572. void FrameWindow::minimize() { start_func
  573. if (state == FRAME_STATE_MINIMIZED) return;
  574. char* button = strchr(titlebarButtons, 'h');
  575. if (!button) return; // Can't minimize!
  576. if (state == FRAME_STATE_MAXIMIZED) restore();
  577. restoreX = x;
  578. restoreY = y;
  579. restoreWidth = width;
  580. restoreHeight = height;
  581. state = FRAME_STATE_MINIMIZED;
  582. resize(width, titlebarHeight + 2 * outerBorder);
  583. *button = 'j';
  584. }
  585. void FrameWindow::maximize() { start_func
  586. if ((state == FRAME_STATE_MAXIMIZED) || ((parent != desktop) && (parent != NULL))) return;
  587. char* button = strchr(titlebarButtons, 'g');
  588. if (!button) return; // Can't maximize!
  589. if (state == FRAME_STATE_MINIMIZED) {
  590. char* button2 = strchr(titlebarButtons, 'j');
  591. if (button2) *button2 = 'h';
  592. }
  593. restoreX = x;
  594. restoreY = y;
  595. if (state == FRAME_STATE_NORMAL) {
  596. restoreWidth = width;
  597. restoreHeight = height;
  598. }
  599. // (final -1/+1 is for the inner titlebar border)
  600. state = FRAME_STATE_MAXIMIZED;
  601. move(desktop->desktopX() - outerBorder, desktop->desktopY() - outerBorder - 1);
  602. resize(desktop->desktopWidth() + outerBorder * 2,
  603. desktop->desktopHeight() + outerBorder * 2 + 1);
  604. *button = 'j';
  605. }
  606. void FrameWindow::restore() { start_func
  607. if (state == FRAME_STATE_NORMAL) return;
  608. char* button = strchr(titlebarButtons, 'j');
  609. if (button) *button = state == FRAME_STATE_MAXIMIZED ? 'g' : 'h';
  610. if (state != FRAME_STATE_MAXIMIZED) {
  611. restoreX = x;
  612. restoreY = y;
  613. }
  614. state = FRAME_STATE_NORMAL;
  615. confineSize(restoreX, restoreY, restoreWidth, restoreHeight);
  616. move(restoreX, restoreY);
  617. resize(restoreWidth, restoreHeight);
  618. }
  619. void FrameWindow::toggleToolPanel(int index, int giveFocusIfOpen) { start_func
  620. if ((index < (int)clientArea.size()) && (index > 0) && (clientArea[index].window)) {
  621. int position = clientArea[index].position & ~CLIENT_HIDDEN;
  622. // Ignore floating/desktop (@TODO: fix later)
  623. if ((position & CLIENT_FLOAT) || (position & CLIENT_DOCKED)) {
  624. return;
  625. }
  626. // Toggle closed or not
  627. position ^= CLIENT_CLOSED;
  628. if ((!(position & CLIENT_CLOSED)) && (giveFocusIfOpen)) focusToolPanel(index);
  629. else if ((position & CLIENT_CLOSED) && (subFocus == index)) focusToolPanel(0);
  630. // Redraw
  631. resize(width, height);
  632. }
  633. }
  634. void FrameWindow::focusToolPanel(int index) { start_func
  635. // No focus if not visible or doesn't exist
  636. if (index >= (int)clientArea.size()) index = 0;
  637. if (index > 0) {
  638. if ((!clientArea[index].window) || (clientArea[index].position & CLIENT_NOTMANAGED)) {
  639. index = 0;
  640. }
  641. // Ignore floating/desktop (@TODO: fix later)
  642. if ((clientArea[index].position & CLIENT_FLOAT) || (clientArea[index].position & CLIENT_DOCKED)) {
  643. return;
  644. }
  645. }
  646. // Doesn't match current focus?
  647. if ((index != subFocus) && (haveFocus)) {
  648. // Lose
  649. SDL_Event customEvent;
  650. customEvent.type = SDL_INPUTFOCUS;
  651. customEvent.user.code = 2;
  652. customEvent.user.data1 = NULL;
  653. customEvent.user.data2 = NULL;
  654. if (clientArea[subFocus].window) clientArea[subFocus].window->event(0, &customEvent);
  655. // Gain
  656. subFocus = index;
  657. customEvent.user.code = 1;
  658. if (clientArea[subFocus].window) clientArea[subFocus].window->event(haveFocus, &customEvent);
  659. }
  660. // If we didn't have focus, we still need to set our personal focus
  661. // for when we regain focus later
  662. else {
  663. subFocus = index;
  664. }
  665. }
  666. void FrameWindow::redoSize() { start_func
  667. if (state == FRAME_STATE_MAXIMIZED) {
  668. restore();
  669. maximize();
  670. }
  671. else {
  672. int cX = x;
  673. int cY = y;
  674. int cW = width;
  675. int cH = height;
  676. confineSize(cX, cY, cW, cH);
  677. move(cX, cY);
  678. resize(cW, cH);
  679. }
  680. }
  681. void FrameWindow::updateClientSize() { start_func
  682. resize(width, height);
  683. }
  684. void FrameWindow::setAutoCenter(int newAutoCenter) { start_func
  685. autoCenterScroll = newAutoCenter;
  686. resize(width, height);
  687. }
  688. void FrameWindow::setScroll(int vLine, int hLine) { start_func
  689. assert(vLine > 0);
  690. assert(hLine > 0);
  691. vScrollLine = vLine;
  692. hScrollLine = hLine;
  693. // A page = full height/width, rounded down to a line multiple,
  694. // minus one line's worth
  695. vScrollPage = (clientHeightInner / vLine - 1) * vLine;
  696. if (vScrollPage < vLine) vScrollPage = vLine;
  697. hScrollPage = (clientWidthInner / hLine - 1) * hLine;
  698. if (hScrollPage < hLine) hScrollPage = hLine;
  699. }
  700. void FrameWindow::resize(int newWidth, int newHeight, int newViewWidth, int newViewHeight, int fromParent) { start_func
  701. // If a framepanel, resize to view if from parent (unless minimized)
  702. if (framePanel) {
  703. if ((state != FRAME_STATE_MINIMIZED) && (fromParent)) {
  704. if (newViewWidth == -1) newViewWidth = viewWidth;
  705. if (newViewWidth != -1) newWidth = newViewWidth;
  706. if (newViewHeight == -1) newViewHeight = viewHeight;
  707. if (newViewHeight != -1) newHeight = newViewHeight;
  708. }
  709. }
  710. else {
  711. // (enforce minimum width/height)
  712. if (newHeight < (titlebarHeight + 2 * outerBorder)) {
  713. newHeight = titlebarHeight + 2 * outerBorder;
  714. }
  715. if (newWidth < (outerBorder * 2 + 2 + titlebarButtonTotalWidth)) {
  716. newWidth = outerBorder * 2 + 2 + titlebarButtonTotalWidth;
  717. }
  718. }
  719. if (clientArea[0].window) {
  720. clientActualWidth = clientArea[0].window->getWidth();
  721. clientActualHeight = clientArea[0].window->getHeight();
  722. }
  723. else {
  724. clientActualWidth = clientActualHeight = 0;
  725. }
  726. // Our size (doesn't always set dirty, so we must)
  727. Window::resize(newWidth, newHeight, newViewWidth, newViewHeight, fromParent);
  728. setDirty();
  729. whyDirty = FRAME_DIRTY_ALL;
  730. // Tool panel and reserve sizes and client area size
  731. reorganizeReserves();
  732. // If resizing in SNAP mode-
  733. // Our actual size should fit exactly to hold clientarea 0, if possible- but
  734. // allow it to resize first (unless we're minimized)
  735. if ((resizable == RESIZING_SNAP) &&
  736. (clientArea[0].window) &&
  737. (clientActualWidth > 0) &&
  738. (clientActualHeight > 0) &&
  739. (state != FRAME_STATE_MINIMIZED)) {
  740. clientArea[0].window->resize(clientActualWidth, clientActualHeight, clientArea[0].width, clientArea[0].height, 1);
  741. clientActualWidth = clientArea[0].window->getWidth();
  742. clientActualHeight = clientArea[0].window->getHeight();
  743. if ((clientActualWidth != clientArea[0].width) || (clientActualHeight != clientArea[0].height)) {
  744. newWidth += clientActualWidth - clientArea[0].width;
  745. newHeight += clientActualHeight - clientArea[0].height;
  746. Window::resize(newWidth, newHeight, newViewWidth, newViewHeight, fromParent);
  747. reorganizeReserves();
  748. }
  749. }
  750. // Finish tool panels
  751. int max = clientArea.size();
  752. for (int pos = 1; pos < max; ++pos) {
  753. if (clientArea[pos].window) {
  754. if ((clientArea[pos].position & CLIENT_FLOAT) || (clientArea[pos].position & CLIENT_DOCKED)) {
  755. // Don't resize floating or desktop-docked panels
  756. }
  757. else if (clientArea[pos].position & CLIENT_NOTMANAGED) {
  758. clientArea[pos].window->resize(clientArea[pos].window->getWidth(), clientArea[pos].window->getHeight(), 0, 0, 1);
  759. }
  760. else {
  761. clientArea[pos].window->move(clientArea[pos].x, clientArea[pos].y);
  762. clientArea[pos].window->resize(clientArea[pos].window->getWidth(), clientArea[pos].window->getHeight(), clientArea[pos].widthShown, clientArea[pos].heightShown, 1);
  763. }
  764. }
  765. }
  766. // Scrollbars default to off
  767. vScrollbarX = vScrollbarY = vScrollbarWidth = vScrollbarHeight = vScrollbarTabSize = 0;
  768. hScrollbarX = hScrollbarY = hScrollbarWidth = hScrollbarHeight = hScrollbarTabSize = 0;
  769. // Must have room for at least 3 button sizes, or no scrollbars appear
  770. if ((clientArea[0].window) &&
  771. (clientArea[0].width >= scrollbarButtonSize * 3) &&
  772. (clientArea[0].height >= scrollbarButtonSize * 3)) {
  773. int needH = clientActualWidth > clientArea[0].width;
  774. int needV = clientActualHeight > clientArea[0].height;
  775. if (needV) needH = clientActualWidth > clientArea[0].width - scrollbarButtonSize;
  776. if (needH) needV = clientActualHeight > clientArea[0].height - scrollbarButtonSize;
  777. if (needV) {
  778. vScrollbarX = clientArea[0].x + clientArea[0].width - scrollbarButtonSize;
  779. vScrollbarY = clientArea[0].y;
  780. vScrollbarWidth = scrollbarButtonSize;
  781. vScrollbarHeight = clientArea[0].height;
  782. if (needH) vScrollbarHeight -= scrollbarButtonSize;
  783. }
  784. if (needH) {
  785. hScrollbarX = clientArea[0].x;
  786. hScrollbarY = clientArea[0].y + clientArea[0].height - scrollbarButtonSize;
  787. hScrollbarWidth = clientArea[0].width;
  788. hScrollbarHeight = scrollbarButtonSize;
  789. if (needV) hScrollbarWidth -= scrollbarButtonSize;
  790. }
  791. }
  792. // Assign inner size
  793. clientWidthInner = clientArea[0].width - vScrollbarWidth;
  794. clientHeightInner = clientArea[0].height - hScrollbarHeight;
  795. // Now we can determine scrollbar tab sizes
  796. // Determine tab size as the percent the current view is of the total view
  797. // Tab position determined by scrollTo call later
  798. if (vScrollbarX) {
  799. vScrollbarTabSize = clientHeightInner * (vScrollbarHeight - scrollbarButtonSize * 2) / clientActualHeight;
  800. if (vScrollbarTabSize < FRAME_SCROLLTABMIN) vScrollbarTabSize = FRAME_SCROLLTABMIN;
  801. if (vScrollbarTabSize > (vScrollbarHeight - scrollbarButtonSize * 2)) vScrollbarTabSize = 0;
  802. }
  803. if (hScrollbarY) {
  804. hScrollbarTabSize = clientWidthInner * (hScrollbarWidth - scrollbarButtonSize * 2) / clientActualWidth;
  805. if (hScrollbarTabSize < FRAME_SCROLLTABMIN) hScrollbarTabSize = FRAME_SCROLLTABMIN;
  806. if (hScrollbarTabSize > (hScrollbarWidth - scrollbarButtonSize * 2)) hScrollbarTabSize = 0;
  807. }
  808. // Calculate titlebar size
  809. titlebarWidth = newWidth - (outerBorder * 2);
  810. titlebarButtonX = titlebarX + titlebarWidth - 1 - titlebarButtonTotalWidth;
  811. // Ensure we're scrolled properly and sizing is set right
  812. if ((autoCenterScroll) && (clientActualWidth < clientWidthInner)) {
  813. scrollX = (clientWidthInner - clientActualWidth) / 2;
  814. }
  815. if ((autoCenterScroll) && (clientActualHeight < clientHeightInner)) {
  816. scrollY = (clientHeightInner - clientActualHeight) / 2;
  817. }
  818. scrollTo(scrollX, scrollY);
  819. setScroll(vScrollLine, hScrollLine);
  820. // Tell client of new view size?
  821. if (clientArea[0].window) {
  822. clientArea[0].window->resize(clientActualWidth, clientActualHeight, clientWidthInner, clientHeightInner, 1);
  823. clientArea[0].window->move(clientArea[0].x + scrollX, clientArea[0].y + scrollY);
  824. }
  825. }
  826. FrameWindow::~FrameWindow() { start_func
  827. if (parent == desktop) desktop->removeWindow(this);
  828. vector<ClientArea>::iterator end = clientArea.end();
  829. for (vector<ClientArea>::iterator pos = clientArea.begin(); pos != end; ++pos) {
  830. if ((*pos).window) {
  831. (*pos).window->setParent(NULL);
  832. (*pos).window->setParentNotify(NULL);
  833. if ((*pos).window->wantsToBeDeleted()) delete (*pos).window;
  834. }
  835. }
  836. }
  837. void FrameWindow::resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp) { start_func
  838. Window::resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
  839. vector<ClientArea>::iterator end = clientArea.end();
  840. for (vector<ClientArea>::iterator pos = clientArea.begin(); pos != end; ++pos) {
  841. if ((*pos).window) {
  842. (*pos).window->resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
  843. }
  844. }
  845. }
  846. void FrameWindow::dragFloatPanel(int index, int drop) { start_func
  847. assert(index > 0);
  848. assert(index < (int)clientArea.size());
  849. if ((clientArea[index].window) && (clientArea[index].position & CLIENT_FLOAT)) {
  850. // x/y position of floating panel, relative to our window
  851. int fx = clientArea[index].window->getX() - x;
  852. int fy = clientArea[index].window->getY() - y;
  853. // sensitivity based off size
  854. int sx = clientArea[index].window->getWidth() / 2;
  855. int sy = clientArea[index].window->getHeight() / 2;
  856. // work off of center
  857. fx += sx;
  858. fy += sy;
  859. // scan all panel positions
  860. int closestDist = 99999;
  861. int closestSide = -1;
  862. int closestAfter = -1;
  863. int dist;
  864. vector<ClientArea>::iterator source = clientArea.end();
  865. vector<ClientArea>::iterator closest = clientArea.end();
  866. vector<ClientArea>::iterator end = clientArea.end();
  867. vector<ClientArea>::iterator pos = clientArea.begin();
  868. for (++pos; pos != end; ++pos) {
  869. if ((*pos).window == clientArea[index].window) {
  870. source = pos;
  871. }
  872. else if (((*pos).window) && (!((*pos).position & CLIENT_NOTMANAGED))) {
  873. if ((*pos).position & CLIENT_TOPBOTTOM) {
  874. // Look at left and right centers
  875. if (abs(fy - ((*pos).y + (*pos).heightShown / 2)) < sy) {
  876. if (abs(fx - (*pos).x) < sx) {
  877. dist = abs(fx - (*pos).x) + abs(fy - ((*pos).y + (*pos).heightShown / 2));
  878. if (dist <= closestDist) {
  879. closestDist = dist;
  880. closest = pos;
  881. closestAfter = 0;
  882. floatTargetX = (*pos).x;
  883. }
  884. }
  885. if (abs(fx - ((*pos).x + (*pos).widthShown)) < sx) {
  886. dist = abs(fx - ((*pos).x + (*pos).widthShown)) + abs(fy - ((*pos).y + (*pos).heightShown / 2));
  887. if (dist <= closestDist) {
  888. closestDist = dist;
  889. closest = pos;
  890. closestAfter = 1;
  891. floatTargetX = (*pos).x + (*pos).widthShown;
  892. }
  893. }
  894. if (closest == pos) {
  895. closestSide = (*pos).position;
  896. floatTargetX -= sx;
  897. floatTargetWidth = sx * 2;
  898. floatTargetY = (*pos).y;
  899. floatTargetHeight = (*pos).heightShown;
  900. }
  901. }
  902. }
  903. else {
  904. // Look at top and bottom centers
  905. if (abs(fx - ((*pos).x + (*pos).widthShown / 2)) < sx) {
  906. if (abs(fy - (*pos).y) < sy) {
  907. dist = abs(fy - (*pos).y) + abs(fx - ((*pos).x + (*pos).widthShown / 2));
  908. if (dist <= closestDist) {
  909. closestDist = dist;
  910. closest = pos;
  911. closestAfter = 0;
  912. floatTargetY = (*pos).y;
  913. }
  914. }
  915. if (abs(fy - ((*pos).y + (*pos).heightShown)) < sy) {
  916. dist = abs(fy - ((*pos).y + (*pos).heightShown)) + abs(fx - ((*pos).x + (*pos).widthShown / 2));
  917. if (dist <= closestDist) {
  918. closestDist = dist;
  919. closest = pos;
  920. closestAfter = 1;
  921. floatTargetY = (*pos).y + (*pos).heightShown;
  922. }
  923. }
  924. if (closest == pos) {
  925. closestSide = (*pos).position;
  926. floatTargetY -= sy;
  927. floatTargetHeight = sy * 2;
  928. floatTargetX = (*pos).x;
  929. floatTargetWidth = (*pos).widthShown;
  930. }
  931. }
  932. }
  933. }
  934. }
  935. // Scan any empty reserves also
  936. if (reserve[CLIENT_LEFT] <= 0) {
  937. // Look at left center of client area
  938. if ((abs(fy - (clientArea[0].y + clientArea[0].height / 2)) < sy) && (abs(fx - clientArea[0].x) < sx)) {
  939. dist = abs(fy - (clientArea[0].y + clientArea[0].height / 2)) + abs(fx - clientArea[0].x);
  940. if (dist <= closestDist) {
  941. closestDist = dist;
  942. closestSide = CLIENT_LEFT;
  943. closest = clientArea.end();
  944. floatTargetX = clientArea[0].x - sx;
  945. floatTargetY = clientArea[0].y;
  946. floatTargetWidth = sx * 2;
  947. floatTargetHeight = clientArea[0].height;
  948. }
  949. }
  950. }
  951. if (reserve[CLIENT_RIGHT] <= 0) {
  952. // Look at right center of client area
  953. if ((abs(fy - (clientArea[0].y + clientArea[0].height / 2)) < sy) && (abs(fx - (clientArea[0].x + clientArea[0].width)) < sx)) {
  954. dist = abs(fy - (clientArea[0].y + clientArea[0].height / 2)) + abs(fx - (clientArea[0].x + clientArea[0].width));
  955. if (dist <= closestDist) {
  956. closestDist = dist;
  957. closestSide = CLIENT_RIGHT;
  958. closest = clientArea.end();
  959. floatTargetX = clientArea[0].x + clientArea[0].width - sx;
  960. floatTargetY = clientArea[0].y;
  961. floatTargetWidth = sx * 2;
  962. floatTargetHeight = clientArea[0].height;
  963. }
  964. }
  965. }
  966. if (reserve[CLIENT_TOP] <= 0) {
  967. // Look at top center of client area
  968. if ((abs(fx - (clientArea[0].x + clientArea[0].width / 2)) < sx) && (abs(fy - clientArea[0].y) < sy)) {
  969. dist = abs(fx - (clientArea[0].x + clientArea[0].width / 2)) + abs(fy - clientArea[0].y);
  970. if (dist <= closestDist) {
  971. closestDist = dist;
  972. closestSide = CLIENT_TOP;
  973. closest = clientArea.end();
  974. floatTargetX = clientArea[0].x;
  975. floatTargetY = clientArea[0].y - sy;
  976. floatTargetWidth = clientArea[0].width;
  977. floatTargetHeight = sy * 2;
  978. }
  979. }
  980. }
  981. if (reserve[CLIENT_BOTTOM] <= 0) {
  982. // Look at bottom center of client area
  983. if ((abs(fx - (clientArea[0].x + clientArea[0].width / 2)) < sx) && (abs(fy - (clientArea[0].y + clientArea[0].height)) < sy)) {
  984. dist = abs(fx - (clientArea[0].x + clientArea[0].width / 2)) + abs(fy - (clientArea[0].y + clientArea[0].height));
  985. if (dist <= closestDist) {
  986. closestDist = dist;
  987. closestSide = CLIENT_BOTTOM;
  988. closest = clientArea.end();
  989. floatTargetX = clientArea[0].x;
  990. floatTargetY = clientArea[0].y + clientArea[0].height - sy;
  991. floatTargetWidth = clientArea[0].width;
  992. floatTargetHeight = sy * 2;
  993. }
  994. }
  995. }
  996. if ((closestSide < 0) || (floatTargetWidth <= 0) || (floatTargetHeight <= 0)) {
  997. floatTarget = 0;
  998. }
  999. else {
  1000. if (drop) {
  1001. // Drop panel on this side
  1002. FrameWindow* window = dynamic_cast<FrameWindow*>((*source).window);
  1003. int position = (((*source).position & ~CLIENT_FLOAT) & ~CLIENT_GETSIDE) | closestSide;
  1004. // If relative to an existing panel, sort into list in proper position
  1005. if (closest != clientArea.end()) {
  1006. // adjust element later in list first, to preserve iterators
  1007. if (source > closest) {
  1008. clientArea.erase(source);
  1009. if (closestAfter) ++closest; // (source = closest now? still ok)
  1010. addToolPanel(window, position, &closest);
  1011. }
  1012. else {
  1013. (*source).window = NULL;
  1014. if (closestAfter) ++closest;
  1015. addToolPanel(window, position, &closest);
  1016. clientArea.erase(source);
  1017. }
  1018. // Note that both iterators are likely invalid now
  1019. }
  1020. else {
  1021. clientArea.erase(source);
  1022. addToolPanel(window, position);
  1023. }
  1024. floatTarget = 0;
  1025. resize(width, height, -1, -1);
  1026. }
  1027. else {
  1028. floatTarget = 1;
  1029. }
  1030. }
  1031. setDirty();
  1032. whyDirty = FRAME_DIRTY_ALL;
  1033. }
  1034. }
  1035. void FrameWindow::dragToolPanel(int index, int xMotion, int yMotion, int& dragItemOverflowX, int& dragItemOverflowY) { start_func
  1036. assert(index > 0);
  1037. assert(index < (int)clientArea.size());
  1038. xMotion += dragItemOverflowX;
  1039. dragItemOverflowX = 0;
  1040. yMotion += dragItemOverflowY;
  1041. dragItemOverflowY = 0;
  1042. if ((clientArea[index].window) && (!(clientArea[index].position & CLIENT_NOTMANAGED))) {
  1043. // Dragging off of toolbar? (drag 1/2 of distance perpendicular)
  1044. int dragOff = 0;
  1045. if (clientArea[index].position & CLIENT_TOPBOTTOM) {
  1046. if (abs(yMotion) > clientArea[index].heightShown / 2) {
  1047. dragOff = 1;
  1048. }
  1049. }
  1050. else {
  1051. if (abs(xMotion) > clientArea[index].widthShown / 2) {
  1052. dragOff = 1;
  1053. }
  1054. }
  1055. if (dragOff) {
  1056. // Target desktop position
  1057. int tx, ty;
  1058. tx = x + clientArea[index].x + xMotion - FRAME_OUTERBORDER;
  1059. ty = y + clientArea[index].y + yMotion - FRAME_OUTERBORDER;
  1060. // Float it
  1061. int newX = tx;
  1062. int newY = ty;
  1063. int newWidth = dynamic_cast<FrameWindow*>(clientArea[index].window)->getWidth() + FRAME_OUTERBORDER * 2;
  1064. int newHeight = dynamic_cast<FrameWindow*>(clientArea[index].window)->getHeight() + FRAME_OUTERBORDER * 2;
  1065. dynamic_cast<FrameWindow*>(clientArea[index].window)->setFeatures(RESIZING_NOCHANGE, FRAMETYPE_NOCHANGE, TITLEBAR_NOCHANGE, 1, this);
  1066. dynamic_cast<FrameWindow*>(clientArea[index].window)->setParent(NULL);
  1067. dynamic_cast<FrameWindow*>(clientArea[index].window)->confineSize(newX, newY, newWidth, newHeight);
  1068. clientArea[index].position |= CLIENT_FLOAT;
  1069. dynamic_cast<FrameWindow*>(clientArea[index].window)->show(newX, newY, newWidth, newHeight);
  1070. resize(width, height, -1, -1);
  1071. // Initiate a fake click to continue drag- click must be on exact
  1072. // same relative location within window so determine how far off that is
  1073. tx = dynamic_cast<FrameWindow*>(clientArea[index].window)->getX() - tx;
  1074. ty = dynamic_cast<FrameWindow*>(clientArea[index].window)->getY() - ty;
  1075. // Generate fake event
  1076. SDL_Event customEvent;
  1077. int mx, my;
  1078. SDL_GetMouseState(&mx, &my);
  1079. customEvent.button.x = mx + tx;
  1080. customEvent.button.y = my + ty;
  1081. customEvent.type = SDL_MOUSEBUTTONDOWN;
  1082. customEvent.button.button = SDL_BUTTON_LEFT;
  1083. desktop->preventDoubleClick();
  1084. insertEvent(&customEvent);
  1085. return;
  1086. }
  1087. // Find panel right before us
  1088. // Set sizes of panels before us "in stone" (once we start dragging,
  1089. // the user has locked in the previous sizes as a starting point)
  1090. int previous = 0;
  1091. for (int pos = 1; pos < index; ++pos) {
  1092. if (clientArea[pos].window) {
  1093. if (clientArea[pos].position == clientArea[index].position) {
  1094. if (clientArea[pos].position& CLIENT_TOPBOTTOM) {
  1095. clientArea[pos].width = clientArea[pos].widthShown;
  1096. }
  1097. else {
  1098. clientArea[pos].height = clientArea[pos].heightShown;
  1099. }
  1100. previous = pos;
  1101. }
  1102. }
  1103. }
  1104. // If found, resize it appropriately
  1105. if (previous) {
  1106. int oldSize;
  1107. if (clientArea[index].position & CLIENT_TOPBOTTOM) {
  1108. clientArea[previous].width += xMotion;
  1109. oldSize = clientArea[previous].widthShown + xMotion;
  1110. xMotion = 0;
  1111. }
  1112. else {
  1113. clientArea[previous].height += yMotion;
  1114. oldSize = clientArea[previous].heightShown + yMotion;
  1115. yMotion = 0;
  1116. }
  1117. resize(width, height, -1, -1);
  1118. if (clientArea[index].position & CLIENT_TOPBOTTOM) {
  1119. if (oldSize != clientArea[previous].widthShown) {
  1120. dragItemOverflowX = oldSize - clientArea[previous].widthShown;
  1121. }
  1122. }
  1123. else {
  1124. if (oldSize != clientArea[previous].heightShown) {
  1125. dragItemOverflowY = oldSize - clientArea[previous].heightShown;
  1126. }
  1127. }
  1128. }
  1129. }
  1130. dragItemOverflowX = xMotion;
  1131. dragItemOverflowY = yMotion;
  1132. }
  1133. void FrameWindow::childMoved(int fromX, int fromY, int toX, int toY, Window* child) { start_func
  1134. assert(child);
  1135. setChildDirty();
  1136. }
  1137. void FrameWindow::childResized(int fromW, int fromH, int toW, int toH, Window* child) { start_func
  1138. assert(child);
  1139. // Reaccount for child's new window size as requested
  1140. for (unsigned int pos = 1; pos < clientArea.size(); ++pos) {
  1141. if ((clientArea[pos].window == child) && (!(clientArea[pos].position & CLIENT_NOTMANAGED))) {
  1142. clientArea[pos].width = -1;
  1143. clientArea[pos].height = -1;
  1144. resize(width, height, -1, -1);
  1145. break;
  1146. }
  1147. }
  1148. setChildDirty();
  1149. }
  1150. void FrameWindow::childDeleted(Window* child) { start_func
  1151. assert(child);
  1152. vector<ClientArea>::iterator end = clientArea.end();
  1153. for (vector<ClientArea>::iterator pos = clientArea.begin(); pos != end; ++pos) {
  1154. if ((*pos).window == child) {
  1155. (*pos).window= NULL;
  1156. break;
  1157. }
  1158. }
  1159. }
  1160. void FrameWindow::childModified(Window* child) { start_func
  1161. assert(child);
  1162. vector<ClientArea>::iterator end = clientArea.end();
  1163. for (vector<ClientArea>::iterator pos = clientArea.begin(); pos != end; ++pos) {
  1164. if (((*pos).window) && ((*pos).window != child)) {
  1165. (*pos).window->siblingModified(child);
  1166. }
  1167. }
  1168. Window::childModified(child);
  1169. }
  1170. void FrameWindow::siblingModified(Window* sibling) { start_func
  1171. assert(sibling);
  1172. vector<ClientArea>::iterator end = clientArea.end();
  1173. for (vector<ClientArea>::iterator pos = clientArea.begin(); pos != end; ++pos) {
  1174. if (((*pos).window) && ((*pos).window != sibling)) {
  1175. (*pos).window->siblingModified(sibling);
  1176. }
  1177. }
  1178. }
  1179. void FrameWindow::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
  1180. assert(destSurface);
  1181. if (visible) {
  1182. // This breaks redraw down into titlebar, scrollbars, edge, client areas
  1183. // Redraw all?
  1184. if (whyDirty == FRAME_DIRTY_ALL) {
  1185. getRect(toDisplay);
  1186. toDisplay.x += xOffset;
  1187. toDisplay.y += yOffset;
  1188. dirty = 0;
  1189. intersectRects(toDisplay, clipArea);
  1190. }
  1191. xOffset += x;
  1192. yOffset += y;
  1193. // Anything to draw so far?
  1194. if (toDisplay.w) {
  1195. SDL_SetClipRect(destSurface, &toDisplay);
  1196. // Draw fill and border
  1197. if (outerBorder) {
  1198. drawGuiBox(xOffset, yOffset, width, height, FRAME_BEVELTHICKNESS, destSurface);
  1199. }
  1200. else {
  1201. drawRect(xOffset, yOffset, width, height, guiPacked[COLOR_FILL], destSurface);
  1202. }
  1203. if (((frameType == FRAMETYPE_BEVEL_BK) || (frameType == FRAMETYPE_BEVEL_TEXT)) &&
  1204. (clientArea[0].width > 0) && (clientArea[0].height > 0)) {
  1205. drawGuiBoxInvert(xOffset + clientArea[0].x - FRAME_BEVELTHICKNESS, yOffset + clientArea[0].y - FRAME_BEVELTHICKNESS,
  1206. clientArea[0].width + FRAME_BEVELTHICKNESS * 2, clientArea[0].height + FRAME_BEVELTHICKNESS * 2,
  1207. FRAME_BEVELTHICKNESS, destSurface);
  1208. }
  1209. }
  1210. // This will be the final displayed amount, we update it as we go but still work
  1211. // off of the original dirtied area for efficiency
  1212. Rect updatedArea = toDisplay;
  1213. // Titlebar
  1214. if (titlebarType != TITLEBAR_OFF) {
  1215. // Determine clip area for titlebar
  1216. Rect titleClip = { titlebarX + xOffset, titlebarY + yOffset, titlebarWidth, titlebarHeight };
  1217. intersectRects(titleClip, clipArea);
  1218. // If we need to update title, OR it intersects with the dirty area,
  1219. // redraw it; note that if the second conditional is tested, vertClip will be reduced
  1220. // to whatever portion intersects with the dirty area, which is what we want
  1221. if ((whyDirty & FRAME_DIRTY_TITLE) || (intersectRects(titleClip, toDisplay))) {
  1222. // Add titlebar rect into updated area
  1223. boundRects(updatedArea, titleClip);
  1224. SDL_SetClipRect(destSurface, &titleClip);
  1225. drawGradient(titlebarX + xOffset, titlebarY + yOffset, titlebarWidth - titlebarButtonTotalWidth, titlebarHeight, guiRGB[haveFocus ? COLOR_TITLEACTIVE1 : COLOR_TITLEINACTIVE1], guiRGB[haveFocus ? COLOR_TITLEACTIVE2 : COLOR_TITLEINACTIVE2], destSurface);
  1226. // (fill corners of inverted titlebar box first)
  1227. drawPixel(titlebarX + xOffset + titlebarWidth - 1, titlebarY + yOffset, guiPacked[COLOR_FILL], destSurface);
  1228. drawPixel(titlebarX + xOffset, titlebarY + yOffset + titlebarHeight - 1, guiPacked[COLOR_FILL], destSurface);
  1229. drawGuiBoxInvert(titlebarX + xOffset, titlebarY + yOffset, titlebarWidth, titlebarHeight, 1, destSurface);
  1230. // Buttons
  1231. for (int pos = strlen(titlebarButtons) - 1; pos >= 0; --pos) {
  1232. string text(1, titlebarButtons[pos]);
  1233. if ((dragMode == FRAME_DRAG_BUTTONS) && (dragItem == (DragSubType)(pos + 1))) {
  1234. // Non-gradient: drawRect(xOffset + titlebarButtonX + pos * titlebarButtonSize, yOffset + titlebarButtonY, titlebarButtonSize, titlebarButtonSize, guiPacked[COLOR_FILL], destSurface);
  1235. drawGradient(xOffset + titlebarButtonX + pos * titlebarButtonSize, yOffset + titlebarButtonY, titlebarButtonSize, titlebarButtonSize, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
  1236. drawGuiBoxInvert(xOffset + titlebarButtonX + pos * titlebarButtonSize, yOffset + titlebarButtonY, titlebarButtonSize, titlebarButtonSize, 2, destSurface);
  1237. drawText(text, guiRGB[COLOR_TEXT], xOffset + titlebarButtonX + pos * titlebarButtonSize + titlebarButtonXPad + 1, yOffset + titlebarButtonY + titlebarButtonXPad + 1, destSurface, titlebarType == TITLEBAR_TOOL ? FONT_TOOLTITLEBARWIDGET : FONT_TITLEBARWIDGET);
  1238. }
  1239. else {
  1240. drawGuiBox(xOffset + titlebarButtonX + pos * titlebarButtonSize, yOffset + titlebarButtonY, titlebarButtonSize, titlebarButtonSize, 2, destSurface);
  1241. // Non-gradient: nothing
  1242. drawGradient(xOffset + titlebarButtonX + pos * titlebarButtonSize + 2, yOffset + titlebarButtonY + 2, titlebarButtonSize - 4, titlebarButtonSize - 4, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
  1243. drawText(text, guiRGB[COLOR_TEXT], xOffset + titlebarButtonX + pos * titlebarButtonSize + titlebarButtonXPad, yOffset + titlebarButtonY + titlebarButtonXPad, destSurface, titlebarType == TITLEBAR_TOOL ? FONT_TOOLTITLEBARWIDGET : FONT_TITLEBARWIDGET);
  1244. }
  1245. }
  1246. // (clip text further- done last due to new clip rectangle)
  1247. Rect titleTextClip = { xOffset + titlebarX + 1, yOffset + titlebarY + 1, titlebarWidth - 2 - titlebarButtonTotalWidth, titlebarHeight - 2 };
  1248. intersectRects(titleTextClip, titleClip);
  1249. SDL_SetClipRect(destSurface, &titleTextClip);
  1250. drawText(titlebar, guiRGB[COLOR_TEXT], xOffset + titlebarX + (titlebarType == TITLEBAR_TOOL ? FRAME_TOOLTITLELEFTPAD : FRAME_TITLELEFTPAD), yOffset + titlebarY + (titlebarType == TITLEBAR_TOOL ? FRAME_TOOLTITLETOPPAD : FRAME_TITLETOPPAD), destSurface, titlebarType == TITLEBAR_TOOL ? FONT_TOOLTITLEBAR : FONT_TITLEBAR);
  1251. }
  1252. }
  1253. // (make sure vertical exists before doing more work)
  1254. if (vScrollbarX) {
  1255. // Determine clip area for vertical scrollbar
  1256. Rect vertClip = { vScrollbarX + xOffset, vScrollbarY + yOffset, vScrollbarWidth, vScrollbarHeight };
  1257. intersectRects(vertClip, clipArea);
  1258. // If we need to update vertical scroll, OR it intersects with the dirty area,
  1259. // redraw it; note that if the second conditional is tested, vertClip will be reduced
  1260. // to whatever portion intersects with the dirty area, which is what we want
  1261. if ((whyDirty & FRAME_DIRTY_SCROLLVERT) || (intersectRects(vertClip, toDisplay))) {
  1262. // Add vertical rect into updated area
  1263. boundRects(updatedArea, vertClip);
  1264. // Draw vertical scrollbar
  1265. SDL_SetClipRect(destSurface, &vertClip);
  1266. SDL_FillRect(destSurface, &vertClip, guiPacked[COLOR_SCROLLTRACK]);
  1267. if (vScrollbarTabSize) {
  1268. // Invert if pressed
  1269. if ((dragMode == FRAME_DRAG_SCROLLBAR_VERT) && (dragItem == FRAME_SCROLL_TAB)) {
  1270. drawRect(xOffset + vScrollbarX, yOffset + vScrollbarY + scrollbarButtonSize + vScrollbarTab, scrollbarButtonSize, vScrollbarTabSize, guiPacked[COLOR_FILL], destSurface);
  1271. drawGuiBoxInvert(xOffset + vScrollbarX, yOffset + vScrollbarY + scrollbarButtonSize + vScrollbarTab, scrollbarButtonSize, vScrollbarTabSize, 2, destSurface);
  1272. }
  1273. else {
  1274. drawGuiBox(xOffset + vScrollbarX, yOffset + vScrollbarY + scrollbarButtonSize + vScrollbarTab, scrollbarButtonSize, vScrollbarTabSize, 2, destSurface);
  1275. }
  1276. }
  1277. // if pressed
  1278. if ((dragMode == FRAME_DRAG_SCROLLBAR_VERT) && (dragItem == FRAME_SCROLL_LESS)) {
  1279. // Non-gradient: drawRect(xOffset + vScrollbarX, yOffset + vScrollbarY, scrollbarButtonSize, scrollbarButtonSize, guiPacked[COLOR_FILL], destSurface);
  1280. drawGradient(xOffset + vScrollbarX, yOffset + vScrollbarY, scrollbarButtonSize, scrollbarButtonSize, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
  1281. drawGuiBoxInvert(xOffset + vScrollbarX, yOffset + vScrollbarY, scrollbarButtonSize, scrollbarButtonSize, 2, destSurface);
  1282. drawText(wtButtonUp, guiRGB[COLOR_TEXT], xOffset + vScrollbarX + scrollbarButtonXPad + 1, yOffset + vScrollbarY + scrollbarButtonXPad + 1, destSurface, FONT_WIDGET);
  1283. }
  1284. else {
  1285. drawGuiBox(xOffset + vScrollbarX, yOffset + vScrollbarY, scrollbarButtonSize, scrollbarButtonSize, 2, destSurface);
  1286. // Non-gradient: nothing
  1287. drawGradient(xOffset + vScrollbarX + 2, yOffset + vScrollbarY + 2, scrollbarButtonSize - 4, scrollbarButtonSize - 4, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
  1288. drawText(wtButtonUp, guiRGB[COLOR_TEXT], xOffset + vScrollbarX + scrollbarButtonXPad, yOffset + vScrollbarY + scrollbarButtonXPad, destSurface, FONT_WIDGET);
  1289. }
  1290. // if pressed
  1291. if ((dragMode == FRAME_DRAG_SCROLLBAR_VERT) && (dragItem == FRAME_SCROLL_MORE)) {
  1292. // Non-gradient: drawRect(xOffset + vScrollbarX, yOffset + vScrollbarY + vScrollbarHeight - scrollbarButtonSize, scrollbarButtonSize, scrollbarButtonSize, guiPacked[COLOR_FILL], destSurface);
  1293. drawGradient(xOffset + vScrollbarX, yOffset + vScrollbarY + vScrollbarHeight - scrollbarButtonSize, scrollbarButtonSize, scrollbarButtonSize, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
  1294. drawGuiBoxInvert(xOffset + vScrollbarX, yOffset + vScrollbarY + vScrollbarHeight - scrollbarButtonSize, scrollbarButtonSize, scrollbarButtonSize, 2, destSurface);
  1295. drawText(wtButtonDown, guiRGB[COLOR_TEXT], xOffset + vScrollbarX + scrollbarButtonXPad + 1, yOffset + vScrollbarY + vScrollbarHeight - scrollbarButtonSize + scrollbarButtonXPad + 1, destSurface, FONT_WIDGET);
  1296. }
  1297. else {
  1298. drawGuiBox(xOffset + vScrollbarX, yOffset + vScrollbarY + vScrollbarHeight - scrollbarButtonSize, scrollbarButtonSize, scrollbarButtonSize, 2, destSurface);
  1299. // Non-gradient: nothing
  1300. drawGradient(xOffset + vScrollbarX + 2, yOffset + vScrollbarY + vScrollbarHeight - scrollbarButtonSize + 2, scrollbarButtonSize - 4, scrollbarButtonSize - 4, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
  1301. drawText(wtButtonDown, guiRGB[COLOR_TEXT], xOffset + vScrollbarX + scrollbarButtonXPad, yOffset + vScrollbarY + vScrollbarHeight - scrollbarButtonSize + scrollbarButtonXPad, destSurface, FONT_WIDGET);
  1302. }
  1303. }
  1304. }
  1305. // (make sure horizontal exists before doing more work)
  1306. if (hScrollbarY) {
  1307. // Determine clip area for horizontal scrollbar
  1308. Rect horizClip = { hScrollbarX + xOffset, hScrollbarY + yOffset, hScrollbarWidth, hScrollbarHeight };
  1309. intersectRects(horizClip, clipArea);
  1310. // If we need to update horizontal scroll, OR it intersects with the dirty area,
  1311. // redraw it; note that if the second conditional is tested, vertClip will be reduced
  1312. // to whatever portion intersects with the dirty area, which is what we want
  1313. if ((whyDirty & FRAME_DIRTY_SCROLLHORIZ) || (intersectRects(horizClip, toDisplay))) {
  1314. // Add horizontal rect into updated area
  1315. boundRects(updatedArea, horizClip);
  1316. // Draw horizontal scrollbar
  1317. SDL_SetClipRect(destSurface, &horizClip);
  1318. SDL_FillRect(destSurface, &horizClip, guiPacked[COLOR_SCROLLTRACK]);
  1319. if (hScrollbarTabSize) {
  1320. // Invert if pressed
  1321. if ((dragMode == FRAME_DRAG_SCROLLBAR_VERT) && (dragItem == FRAME_SCROLL_TAB)) {
  1322. drawRect(xOffset + hScrollbarX + scrollbarButtonSize + hScrollbarTab, yOffset + hScrollbarY, hScrollbarTabSize, scrollbarButtonSize, guiPacked[COLOR_FILL], destSurface);
  1323. drawGuiBoxInvert(xOffset + hScrollbarX + scrollbarButtonSize + hScrollbarTab, yOffset + hScrollbarY, hScrollbarTabSize, scrollbarButtonSize, 2, destSurface);
  1324. }
  1325. else {
  1326. drawGuiBox(xOffset + hScrollbarX + scrollbarButtonSize + hScrollbarTab, yOffset + hScrollbarY, hScrollbarTabSize, scrollbarButtonSize, 2, destSurface);
  1327. }
  1328. }
  1329. // if pressed
  1330. if ((dragMode == FRAME_DRAG_SCROLLBAR_HORIZ) && (dragItem == FRAME_SCROLL_LESS)) {
  1331. // Non-gradient: drawRect(xOffset + hScrollbarX, yOffset + hScrollbarY, scrollbarButtonSize, scrollbarButtonSize, guiPacked[COLOR_FILL], destSurface);
  1332. drawGradient(xOffset + hScrollbarX, yOffset + hScrollbarY, scrollbarButtonSize, scrollbarButtonSize, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
  1333. drawGuiBoxInvert(xOffset + hScrollbarX, yOffset + hScrollbarY, scrollbarButtonSize, scrollbarButtonSize, 2, destSurface);
  1334. drawText(wtButtonLeft, guiRGB[COLOR_TEXT], xOffset + hScrollbarX + scrollbarButtonXPad + 1, yOffset + hScrollbarY + scrollbarButtonXPad + 1, destSurface, FONT_WIDGET);
  1335. }
  1336. else {
  1337. drawGuiBox(xOffset + hScrollbarX, yOffset + hScrollbarY, scrollbarButtonSize, scrollbarButtonSize, 2, destSurface);
  1338. // Non-gradient: nothing
  1339. drawGradient(xOffset + hScrollbarX + 2, yOffset + hScrollbarY + 2, scrollbarButtonSize - 4, scrollbarButtonSize - 4, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
  1340. drawText(wtButtonLeft, guiRGB[COLOR_TEXT], xOffset + hScrollbarX + scrollbarButtonXPad, yOffset + hScrollbarY + scrollbarButtonXPad, destSurface, FONT_WIDGET);
  1341. }
  1342. // if pressed
  1343. if ((dragMode == FRAME_DRAG_SCROLLBAR_HORIZ) && (dragItem == FRAME_SCROLL_MORE)) {
  1344. // Non-gradient: drawRect(xOffset + hScrollbarX + hScrollbarWidth - scrollbarButtonSize, yOffset + hScrollbarY, scrollbarButtonSize, scrollbarButtonSize, guiPacked[COLOR_FILL], destSurface);
  1345. drawGradient(xOffset + hScrollbarX + hScrollbarWidth - scrollbarButtonSize, yOffset + hScrollbarY, scrollbarButtonSize, scrollbarButtonSize, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
  1346. drawGuiBoxInvert(xOffset + hScrollbarX + hScrollbarWidth - scrollbarButtonSize, yOffset + hScrollbarY, scrollbarButtonSize, scrollbarButtonSize, 2, destSurface);
  1347. drawText(wtButtonRight, guiRGB[COLOR_TEXT], xOffset + hScrollbarX + hScrollbarWidth - scrollbarButtonSize + scrollbarButtonXPad + 1, yOffset + hScrollbarY + scrollbarButtonXPad + 1, destSurface, FONT_WIDGET);
  1348. }
  1349. else {
  1350. drawGuiBox(xOffset + hScrollbarX + hScrollbarWidth - scrollbarButtonSize, yOffset + hScrollbarY, scrollbarButtonSize, scrollbarButtonSize, 2, destSurface);
  1351. // Non-gradient: nothing
  1352. drawGradient(xOffset + hScrollbarX + hScrollbarWidth - scrollbarButtonSize + 2, yOffset + hScrollbarY + 2, scrollbarButtonSize - 4, scrollbarButtonSize - 4, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
  1353. drawText(wtButtonRight, guiRGB[COLOR_TEXT], xOffset + hScrollbarX + hScrollbarWidth - scrollbarButtonSize + scrollbarButtonXPad, yOffset + hScrollbarY + scrollbarButtonXPad, destSurface, FONT_WIDGET);
  1354. }
  1355. }
  1356. }
  1357. // Fill in corner?
  1358. if ((vScrollbarX) && (hScrollbarY)) {
  1359. Rect cornerClip = { vScrollbarX + xOffset, hScrollbarY + yOffset, vScrollbarWidth, hScrollbarHeight };
  1360. intersectRects(cornerClip, clipArea);
  1361. // Only needed if area is dirty
  1362. if (intersectRects(cornerClip, toDisplay)) {
  1363. // We set clip because it may have been reset earlier
  1364. SDL_SetClipRect(destSurface, &cornerClip);
  1365. SDL_FillRect(destSurface, &cornerClip, guiPacked[COLOR_FILL]);
  1366. }
  1367. }
  1368. // Panel areas?
  1369. int max = clientArea.size();
  1370. for (int pos = 1; pos < max; ++pos) {
  1371. if ((!(clientArea[pos].position & CLIENT_NOTMANAGED)) && (clientArea[pos].window)) {
  1372. // Panel area that CAN be drawn
  1373. Rect panelClip = { clientArea[pos].x + xOffset, clientArea[pos].y + yOffset, clientArea[pos].widthShown, clientArea[pos].heightShown };
  1374. intersectRects(panelClip, clipArea);
  1375. // Panel area that MUST be drawn
  1376. Rect panelDirty = panelClip;
  1377. // If we need to update panel, OR it intersects with the dirty area, redraw it
  1378. // We test dirty first, because we always want the dirty area to be what
  1379. // the panel HAS to update, even if they're dirty...
  1380. if ((intersectRects(panelDirty, toDisplay)) || (clientArea[pos].window->isDirty())) {
  1381. // ... but we clip to a wider area- the area the panel CAN update
  1382. SDL_SetClipRect(destSurface, &panelClip);
  1383. // Background fill on dirty area
  1384. SDL_FillRect(destSurface, &panelDirty, guiPacked[COLOR_FILL]);
  1385. // Paint panel area
  1386. clientArea[pos].window->display(destSurface, panelDirty, panelClip, xOffset, yOffset);
  1387. // Add panel rect into updated area
  1388. boundRects(updatedArea, panelDirty);
  1389. }
  1390. }
  1391. }
  1392. // Client area exists? (done separate due to 'inner' setting)
  1393. if ((clientWidthInner > 0) && (clientHeightInner > 0)) {
  1394. // Client area that CAN be drawn
  1395. Rect clientClip = { clientArea[0].x + xOffset, clientArea[0].y + yOffset, clientWidthInner, clientHeightInner };
  1396. intersectRects(clientClip, clipArea);
  1397. // Client area that MUST be drawn
  1398. Rect clientDirty = clientClip;
  1399. // If we need to update client, OR it intersects with the dirty area, redraw it
  1400. int clientIsDirty = 0;
  1401. if ((clientArea[0].window) && (clientArea[0].window->isDirty())) clientIsDirty = 1;
  1402. // We test dirty first, because we always want the dirty area to be what
  1403. // the client HAS to update, even if they're dirty...
  1404. if ((intersectRects(clientDirty, toDisplay)) || (clientIsDirty)) {
  1405. // ... but we clip to a wider area- the area the client CAN update
  1406. SDL_SetClipRect(destSurface, &clientClip);
  1407. // Background fill on dirty area
  1408. SDL_FillRect(destSurface, &clientDirty, guiPacked[frameType == FRAMETYPE_BEVEL_BK ? COLOR_BKFILL : frameType == FRAMETYPE_BEVEL_TEXT ? COLOR_TEXTBOX : COLOR_FILL]);
  1409. // Paint client area
  1410. if (clientArea[0].window) {
  1411. clientArea[0].window->display(destSurface, clientDirty, clientClip, xOffset, yOffset);
  1412. // Add client rect into updated area
  1413. boundRects(updatedArea, clientDirty);
  1414. }
  1415. }
  1416. }
  1417. // Float target?
  1418. if (floatTarget) {
  1419. SDL_SetClipRect(destSurface, &toDisplay);
  1420. drawSelectRect(xOffset + floatTargetX, yOffset + floatTargetY, floatTargetWidth, floatTargetHeight, guiPacked[COLOR_SELECTION1], destSurface);
  1421. }
  1422. toDisplay = updatedArea;
  1423. }
  1424. dirty = 0;
  1425. childDirty = 0;
  1426. whyDirty = FRAME_DIRTY_NONE;
  1427. }
  1428. /* Not currently used or supported
  1429. int FrameWindow::usesAlpha() { start_func
  1430. int uses = 0;
  1431. for (int cl = 0; cl <= 2; ++cl) {
  1432. if (clientArea[cl].window) uses = uses || clientArea[cl].window->usesAlpha();
  1433. }
  1434. return uses;
  1435. }
  1436. */
  1437. Window::WindowSort FrameWindow::windowSort() const { start_func
  1438. if (clientArea[0].window != NULL) return clientArea[0].window->windowSort();
  1439. return WINDOWSORT_NORMAL;
  1440. }
  1441. Window::CommandSupport FrameWindow::supportsCommand(int code) const { start_func
  1442. // Frame window commands
  1443. if (code == WINDOW_MIN) return ((state == FRAME_STATE_MINIMIZED) || strchr(titlebarButtons, 'h')) ? Window::COMMAND_ENABLE : Window::COMMAND_DISABLE;
  1444. if (code == WINDOW_MAX) return ((state == FRAME_STATE_MAXIMIZED) || strchr(titlebarButtons, 'g')) ? Window::COMMAND_ENABLE : Window::COMMAND_DISABLE;
  1445. if (code == WINDOW_SWITCHPANEL) {
  1446. int max = clientArea.size();
  1447. for (int pos = 1; pos < max; ++pos) {
  1448. if (clientArea[pos].window) {
  1449. // @TODO: should this also work on floating/desktop panels?
  1450. if (!(clientArea[pos].position & CLIENT_NOTMANAGED)) return Window::COMMAND_ENABLE;
  1451. }
  1452. }
  1453. return Window::COMMAND_HIDE;
  1454. }
  1455. // @TODO: VIEW_TOOLPANEL/VIEW_* to toggle panel visibility
  1456. if ((code == WINDOW_CLOSE) || (code == WINDOW_NEXT) || (code == WINDOW_PREV) ||
  1457. (code == WINDOW_CLOSEALL) || (code == WINDOW_CLOSEALLBUT) || (code == WINDOW_TILEHORIZ) ||
  1458. (code == WINDOW_TILEVERT) || (code == WINDOW_CASCADE)) return Window::COMMAND_ENABLE;
  1459. CommandSupport uses = Window::COMMAND_HIDE;
  1460. vector<ClientArea>::const_iterator end = clientArea.end();
  1461. for (vector<ClientArea>::const_iterator pos = clientArea.begin(); pos != clientArea.end(); ++pos) {
  1462. if ((*pos).window) {
  1463. if (uses == Window::COMMAND_HIDE) uses = (*pos).window->supportsCommand(code);
  1464. else {
  1465. CommandSupport sc = (*pos).window->supportsCommand(code);
  1466. if (sc & Window::COMMAND_ENABLE) return sc;
  1467. }
  1468. }
  1469. }
  1470. return uses;
  1471. }
  1472. int FrameWindow::wantsToBeDeleted() const { start_func
  1473. return deleteMe;
  1474. }
  1475. void FrameWindow::setWantsToBeDeleted(int fDeleteMe) { start_func
  1476. deleteMe = fDeleteMe;
  1477. }
  1478. int FrameWindow::attemptClose() { start_func
  1479. if (clientArea[0].window != NULL) {
  1480. return clientArea[0].window->attemptClose();
  1481. }
  1482. return 1;
  1483. }
  1484. void FrameWindow::scrollToView(int sX, int sY, int sWidth, int sHeight) { start_func
  1485. int cX = -scrollX;
  1486. int cY = -scrollY;
  1487. // Don't adjust left-right if target encompasses entire view
  1488. if ((sX > cX) || (sX + sWidth < cX + clientWidthInner)) {
  1489. // If right-most edge of target is to the right of us, scroll to see it
  1490. if (sX + sWidth > cX + clientWidthInner) cX = sX + sWidth - clientWidthInner;
  1491. // Same for left
  1492. if (sX < cX) cX = sX;
  1493. }
  1494. // Don't adjust up-down if target encompasses entire view
  1495. if ((sY > cY) || (sY + sHeight < cY + clientHeightInner)) {
  1496. // Same for bottom, top
  1497. if (sY + sHeight > cY + clientHeightInner) cY = sY + sHeight - clientHeightInner;
  1498. if (sY < cY) cY = sY;
  1499. }
  1500. // Complete scroll
  1501. scrollTo(-cX, -cY);
  1502. }
  1503. void FrameWindow::scrollBy(int sX, int sY) { start_func
  1504. scrollTo(scrollX + sX, scrollY + sY);
  1505. }
  1506. void FrameWindow::scrollTo(int sX, int sY, int tabX, int tabY) { start_func
  1507. if (clientArea[0].window) {
  1508. // Not too far in either direction
  1509. if (sX < min(0, clientWidthInner - clientActualWidth)) sX = min(0, clientWidthInner - clientActualWidth);
  1510. if (sY < min(0, clientHeightInner - clientActualHeight)) sY = min(0, clientHeightInner - clientActualHeight);
  1511. // Check this direction last in case window is too large overall
  1512. // Only allow scrolling "positive" if autocenter enabled
  1513. if (autoCenterScroll) {
  1514. if (sX > max(0, clientWidthInner - clientActualWidth)) sX = max(0, clientWidthInner - clientActualWidth);
  1515. if (sY > max(0, clientHeightInner - clientActualHeight)) sY = max(0, clientHeightInner - clientActualHeight);
  1516. }
  1517. else {
  1518. if (sX > 0) sX = 0;
  1519. if (sY > 0) sY = 0;
  1520. }
  1521. // Tab positions
  1522. if ((vScrollbarTabSize) && (clientHeightInner < clientActualHeight)) {
  1523. int newTab;
  1524. if (tabY >= 0) newTab = tabY;
  1525. else newTab = (vScrollbarHeight - (scrollbarButtonSize * 2) - vScrollbarTabSize) * sY / (clientHeightInner - clientActualHeight);
  1526. if (newTab != vScrollbarTab) {
  1527. setDirty();
  1528. whyDirty |= FRAME_DIRTY_SCROLLVERT;
  1529. }
  1530. vScrollbarTab = newTab;
  1531. }
  1532. else {
  1533. if (vScrollbarTab != 0) {
  1534. setDirty();
  1535. whyDirty |= FRAME_DIRTY_SCROLLVERT;
  1536. }
  1537. vScrollbarTab = 0;
  1538. }
  1539. if ((hScrollbarTabSize) && (clientWidthInner < clientActualWidth)) {
  1540. int newTab;
  1541. if (tabX >= 0) newTab = tabX;
  1542. else newTab = (hScrollbarWidth - (scrollbarButtonSize * 2) - hScrollbarTabSize) * sX / (clientWidthInner - clientActualWidth);
  1543. if (newTab != hScrollbarTab) {
  1544. setDirty();
  1545. whyDirty |= FRAME_DIRTY_SCROLLHORIZ;
  1546. }
  1547. hScrollbarTab = newTab;
  1548. }
  1549. else {
  1550. if (hScrollbarTab != 0) {
  1551. setDirty();
  1552. whyDirty |= FRAME_DIRTY_SCROLLHORIZ;
  1553. }
  1554. hScrollbarTab = 0;
  1555. }
  1556. if ((scrollX != sX) || (scrollY != sY)) {
  1557. scrollX = sX;
  1558. scrollY = sY;
  1559. clientArea[0].window->move(clientArea[0].x + scrollX, clientArea[0].y + scrollY);
  1560. }
  1561. }
  1562. else {
  1563. // No client, no scrollbars- reset to zero- dirty if wasn't already at zero
  1564. if ((scrollX) || (scrollY)) {
  1565. setDirty();
  1566. whyDirty = FRAME_DIRTY_ALL;
  1567. }
  1568. scrollX = scrollY = 0;
  1569. }
  1570. }
  1571. void FrameWindow::confineSize(int& sX, int& sY, int& sWidth, int& sHeight) const { start_func
  1572. int clipX, clipY, clipW, clipH;
  1573. // Size is not confined by us if we aren't on desktop
  1574. if ((parent != desktop) && (parent != NULL)) return;
  1575. if (windowSort() >= WINDOWSORT_MODAL) {
  1576. clipX = 0;
  1577. clipY = 0;
  1578. clipW = screenWidth;
  1579. clipH = screenHeight;
  1580. }
  1581. else {
  1582. clipX = desktop->desktopX();
  1583. clipY = desktop->desktopY();
  1584. clipW = desktop->desktopWidth();
  1585. clipH = desktop->desktopHeight();
  1586. }
  1587. // Ensure fits on-screen
  1588. if (sX + sWidth > clipX + clipW) {
  1589. sX = clipX + clipW - sWidth;
  1590. }
  1591. if (sX < clipX) {
  1592. sX = clipX;
  1593. if (sWidth > clipW) sWidth = clipW;
  1594. }
  1595. if (sY + sHeight > clipY + clipH) {
  1596. sY = clipY + clipH - sHeight;
  1597. }
  1598. if (sY < clipY) {
  1599. sY = clipY;
  1600. if (sHeight > clipH) sHeight = clipH;
  1601. }
  1602. // Ensure at least minimum size
  1603. if (sHeight < (titlebarHeight + 2 * outerBorder)) {
  1604. sHeight = (titlebarHeight + 2 * outerBorder);
  1605. }
  1606. if (sWidth < (outerBorder * 2 + 2 + titlebarButtonTotalWidth)) {
  1607. sWidth = (outerBorder * 2 + 2 + titlebarButtonTotalWidth);
  1608. }
  1609. }
  1610. void FrameWindow::show(int xPos, int yPos, int fWidth, int fHeight) { start_func
  1611. assert(xPos >= SHOW_CURRMIN);
  1612. assert(yPos >= SHOW_CURRMIN);
  1613. assert(fWidth >= SHOW_CURRMIN);
  1614. assert(fHeight >= SHOW_CURRMIN);
  1615. int cascadeOffset = outerBorder + titlebarHeight;
  1616. if (fWidth == SHOW_CASCADE) fWidth = desktop->desktopWidth() - cascadeOffset * 3;
  1617. if (fHeight == SHOW_CASCADE) fHeight = desktop->desktopHeight() - cascadeOffset * 3;
  1618. if (fWidth == SHOW_CURRENT) fWidth = width;
  1619. if (fHeight == SHOW_CURRENT) fHeight = height;
  1620. if (fWidth == SHOW_CURRMIN) {
  1621. fWidth = width;
  1622. if (fWidth < DEFAULT_MIN_WIDTH) fWidth = DEFAULT_MIN_WIDTH;
  1623. }
  1624. if (fHeight == SHOW_CURRMIN) {
  1625. fHeight = height;
  1626. if (fHeight < DEFAULT_MIN_HEIGHT) fHeight = DEFAULT_MIN_HEIGHT;
  1627. }
  1628. if ((xPos == SHOW_CASCADE) || (yPos == SHOW_CASCADE)) {
  1629. if (++cascadeStep > 3) cascadeStep = 0;
  1630. }
  1631. if (xPos == SHOW_CASCADE) {
  1632. xPos = desktop->desktopX() + cascadeStep * cascadeOffset;
  1633. }
  1634. if (yPos == SHOW_CASCADE) {
  1635. yPos = desktop->desktopY() + cascadeStep * cascadeOffset;
  1636. }
  1637. if ((xPos == SHOW_CURRENT) || (xPos == SHOW_CURRMIN)) xPos = x;
  1638. if ((yPos == SHOW_CURRENT) || (yPos == SHOW_CURRMIN)) yPos = y;
  1639. confineSize(xPos, yPos, fWidth, fHeight);
  1640. // Move
  1641. resize(fWidth, fHeight);
  1642. move(xPos, yPos);
  1643. desktop->addWindow(this, 1);
  1644. }
  1645. void FrameWindow::hide() { start_func
  1646. // If we aren't part of desktop, this is harmless
  1647. desktop->removeWindow(this);
  1648. }
  1649. void FrameWindow::undoNotify(int undoType, int undoItemId, int undoItemSubId, int uX, int uY, int uW, int uH) { start_func
  1650. if (clientArea[0].window) clientArea[0].window->undoNotify(undoType, undoItemId, undoItemSubId, uX, uY, uW, uH);
  1651. }
  1652. const char* FrameWindow::tooltip(int xPos, int yPos) const { start_func
  1653. DragType toolType;
  1654. DragSubType toolItem;
  1655. whereCoords(xPos, yPos, &toolType, &toolItem);
  1656. if (toolType == FRAME_DRAG_CLIENT) {
  1657. xPos -= clientArea[toolItem].x;
  1658. yPos -= clientArea[toolItem].y;
  1659. if (toolItem == 0) {
  1660. xPos += scrollX;
  1661. yPos += scrollY;
  1662. }
  1663. return clientArea[toolItem].window->tooltip(xPos, yPos);
  1664. }
  1665. if (toolType == FRAME_DRAG_BUTTONS) {
  1666. switch (titlebarButtons[toolItem - 1]) {
  1667. case 'h':
  1668. return "Roll-up window (minimize)";
  1669. case 'g':
  1670. return "Maximize window";
  1671. case 'f':
  1672. return "Close window";
  1673. case 'j':
  1674. return "Restore window";
  1675. }
  1676. }
  1677. return NULL;
  1678. }
  1679. void FrameWindow::whereCoords(int x, int y, DragType* dragType, DragSubType* dragItem) const { start_func
  1680. assert(dragType);
  1681. assert(dragItem);
  1682. *dragType = FRAME_DRAG_NONE;
  1683. *dragItem = FRAME_RESIZE_NONE;
  1684. // Main client area? Client *can* receive clicks outside it's actual area
  1685. if ((clientArea[0].window) &&
  1686. (x >= clientArea[0].x) &&
  1687. (y >= clientArea[0].y) &&
  1688. (x < clientArea[0].x + clientWidthInner) &&
  1689. (y < clientArea[0].y + clientHeightInner)) {
  1690. *dragType = FRAME_DRAG_CLIENT;
  1691. *dragItem = (DragSubType)0;
  1692. }
  1693. // Buttons?
  1694. // (check before titlebar as space is shared)
  1695. else if ((titlebarType != TITLEBAR_OFF) &&
  1696. (x >= titlebarButtonX) &&
  1697. (y >= titlebarButtonY) &&
  1698. (x < titlebarButtonX + titlebarButtonTotalWidth) &&
  1699. (y < titlebarButtonY + titlebarButtonSize)) {
  1700. *dragType = FRAME_DRAG_BUTTONS;
  1701. // (add 1 because 0 is a 'none' value)
  1702. *dragItem = (DragSubType)((x - titlebarButtonX) / titlebarButtonSize + 1);
  1703. }
  1704. // Titlebar?
  1705. else if ((titlebarType != TITLEBAR_OFF) &&
  1706. (x >= titlebarX) &&
  1707. (y >= titlebarY) &&
  1708. (x < titlebarX + titlebarWidth) &&
  1709. (y < titlebarY + titlebarHeight)) {
  1710. *dragType = FRAME_DRAG_TITLE;
  1711. }
  1712. // Vertical scrollbar?
  1713. else if ((x >= vScrollbarX) &&
  1714. (x < vScrollbarX + vScrollbarWidth) &&
  1715. (y >= vScrollbarY) &&
  1716. (y < vScrollbarY + vScrollbarHeight)) {
  1717. *dragType = FRAME_DRAG_SCROLLBAR_VERT;
  1718. if (y < vScrollbarY + scrollbarButtonSize) *dragItem = FRAME_SCROLL_LESS;
  1719. else if (y >= vScrollbarY + vScrollbarHeight - scrollbarButtonSize) *dragItem = FRAME_SCROLL_MORE;
  1720. else if (y < vScrollbarY + scrollbarButtonSize + vScrollbarTab) *dragItem = FRAME_SCROLL_PAGELESS;
  1721. else if (y >= vScrollbarY + scrollbarButtonSize + vScrollbarTab + vScrollbarTabSize) *dragItem = FRAME_SCROLL_PAGEMORE;
  1722. else *dragItem = FRAME_SCROLL_TAB;
  1723. }
  1724. // Horizontal scrollbar?
  1725. else if ((x >= hScrollbarX) &&
  1726. (x < hScrollbarX + hScrollbarWidth) &&
  1727. (y >= hScrollbarY) &&
  1728. (y < hScrollbarY + hScrollbarHeight)) {
  1729. *dragType = FRAME_DRAG_SCROLLBAR_HORIZ;
  1730. if (x < hScrollbarX + scrollbarButtonSize) *dragItem = FRAME_SCROLL_LESS;
  1731. else if (x >= hScrollbarX + hScrollbarWidth - scrollbarButtonSize) *dragItem = FRAME_SCROLL_MORE;
  1732. else if (x < hScrollbarX + scrollbarButtonSize + hScrollbarTab) *dragItem = FRAME_SCROLL_PAGELESS;
  1733. else if (x >= hScrollbarX + scrollbarButtonSize + hScrollbarTab + hScrollbarTabSize) *dragItem = FRAME_SCROLL_PAGEMORE;
  1734. else *dragItem = FRAME_SCROLL_TAB;
  1735. }
  1736. else {
  1737. // Tool panels?
  1738. int max = clientArea.size();
  1739. for (int pos = 1; pos < max; ++pos) {
  1740. if (clientArea[pos].window) {
  1741. if (!(clientArea[pos].position & CLIENT_NOTMANAGED)) {
  1742. if ((x >= clientArea[pos].x) &&
  1743. (y >= clientArea[pos].y) &&
  1744. (x < clientArea[pos].x + clientArea[pos].widthShown) &&
  1745. (y < clientArea[pos].y + clientArea[pos].heightShown) &&
  1746. (x < clientArea[pos].x + clientArea[pos].window->getWidth()) &&
  1747. (y < clientArea[pos].y + clientArea[pos].window->getHeight())) {
  1748. *dragType = FRAME_DRAG_CLIENT;
  1749. *dragItem = (DragSubType)pos;
  1750. return;
  1751. }
  1752. }
  1753. }
  1754. }
  1755. // Resize?
  1756. // (no resizing when maximized, minimized, no border, or prevented)
  1757. if (state != FRAME_STATE_NORMAL) return;
  1758. if ((!resizable) || (!outerBorder)) return;
  1759. DragSubType resizing = FRAME_RESIZE_NONE;
  1760. int sensitive = outerBorder + FRAME_INNERBORDER;
  1761. if ((y >= 0) && (y < sensitive)) {
  1762. resizing = (DragSubType)(resizing | FRAME_RESIZE_TOP);
  1763. sensitive = RESIZE_CORNER_SENSITIVITY;
  1764. }
  1765. if ((y >= height - sensitive) && (y < height)) {
  1766. resizing = (DragSubType)(resizing | FRAME_RESIZE_BOTTOM);
  1767. sensitive = RESIZE_CORNER_SENSITIVITY;
  1768. }
  1769. if ((x >= 0) && (x < sensitive)) {
  1770. resizing = (DragSubType)(resizing | FRAME_RESIZE_LEFT);
  1771. sensitive = RESIZE_CORNER_SENSITIVITY;
  1772. }
  1773. if ((x >= width - sensitive) && (x < width)) {
  1774. resizing = (DragSubType)(resizing | FRAME_RESIZE_RIGHT);
  1775. sensitive = RESIZE_CORNER_SENSITIVITY;
  1776. }
  1777. // Redo the Ys due to possibile sensitivity change from finding an X match
  1778. if ((y >= 0) && (y < sensitive))
  1779. resizing = (DragSubType)(resizing | FRAME_RESIZE_TOP);
  1780. if ((y >= height - sensitive) && (y < height))
  1781. resizing = (DragSubType)(resizing | FRAME_RESIZE_BOTTOM);
  1782. if (resizing) {
  1783. *dragType = FRAME_DRAG_BORDER;
  1784. *dragItem = resizing;
  1785. }
  1786. }
  1787. }
  1788. int FrameWindow::event(int hasFocus, const SDL_Event* event) { start_func
  1789. assert(event);
  1790. DragType newDragMode;
  1791. DragSubType newDragItem;
  1792. int pos;
  1793. int used = 0;
  1794. Uint32 ticks;
  1795. int mX, mY;
  1796. int done = 0;
  1797. Window* exempt = NULL;
  1798. // We need to copy the event if we expect to change it to send a copy to client
  1799. SDL_Event clientEvent = *event;
  1800. switch (event->type) {
  1801. case SDL_CLOSE:
  1802. // Close client(s), then us
  1803. for (pos = 0; pos < (int)clientArea.size(); ++pos) {
  1804. if (clientArea[pos].window) {
  1805. // Shouldn't delete itself here
  1806. clientArea[pos].window->event(hasFocus && (subFocus == pos), &clientEvent);
  1807. // Prevent it from notifying us when it DOES delete itself ("we know")
  1808. clientArea[pos].window->setParent(NULL);
  1809. clientArea[pos].window->setParentNotify(NULL);
  1810. if (clientArea[pos].window->wantsToBeDeleted()) delete clientArea[pos].window;
  1811. clientArea[pos].window = NULL;
  1812. }
  1813. }
  1814. if (parent == desktop) {
  1815. desktop->removeWindow(this);
  1816. // Make sure next frame window is selected
  1817. pos = 0;
  1818. while (desktop->findWindow(WINDOW_FRAME, pos) != NULL) {
  1819. ++pos;
  1820. }
  1821. if (--pos >= 0) {
  1822. desktop->bringToTop(desktop->findWindow(WINDOW_FRAME, pos));
  1823. }
  1824. }
  1825. return 1;
  1826. case SDL_MOUSEBUTTONUP:
  1827. if (dragMode == FRAME_DRAG_CLIENT) {
  1828. // Adjust coordinates to 0-based for client
  1829. if (clientArea[dragItem].window) {
  1830. clientEvent.button.x -= clientArea[dragItem].x + (dragItem == 0 ? scrollX : 0);
  1831. clientEvent.button.y -= clientArea[dragItem].y + (dragItem == 0 ? scrollY : 0);
  1832. // Propogate to client
  1833. clientArea[dragItem].window->event(hasFocus && (subFocus == dragItem), &clientEvent);
  1834. }
  1835. if ((SDL_GetMouseState(NULL, NULL) & (SDL_BUTTON_LMASK | SDL_BUTTON_RMASK)) == 0) done = 1;
  1836. }
  1837. else if (event->button.button == SDL_BUTTON_LEFT) {
  1838. // Button pressed?
  1839. if ((dragMode == FRAME_DRAG_BUTTONS) && (dragItem)) {
  1840. switch (titlebarButtons[(int)(dragItem - 1)]) {
  1841. case 'h':
  1842. minimize();
  1843. break;
  1844. case 'g':
  1845. maximize();
  1846. break;
  1847. case 'f':
  1848. closeWindow(); // Calls attemptClose
  1849. break;
  1850. case 'j':
  1851. restore();
  1852. break;
  1853. }
  1854. setDirty();
  1855. whyDirty |= FRAME_DIRTY_TITLE;
  1856. }
  1857. else if (dragMode == FRAME_DRAG_SCROLLBAR_HORIZ) {
  1858. setDirty();
  1859. whyDirty |= FRAME_DIRTY_SCROLLHORIZ;
  1860. }
  1861. else if (dragMode == FRAME_DRAG_SCROLLBAR_VERT) {
  1862. setDirty();
  1863. whyDirty |= FRAME_DIRTY_SCROLLVERT;
  1864. }
  1865. else if (dragMode == FRAME_DRAG_TITLE) {
  1866. if ((framePanel) && (parent == desktop)) {
  1867. int index = framePanel->findToolPanel(this);
  1868. if (index) framePanel->dragFloatPanel(index, 1);
  1869. }
  1870. }
  1871. done = 1;
  1872. }
  1873. if (done) {
  1874. scrollRepeatTicks = scrollRepeatDelay = 0;
  1875. dragMode = FRAME_DRAG_NONE;
  1876. doMouseHover(event->motion.x, event->motion.y, hasFocus, clientEvent);
  1877. }
  1878. break;
  1879. case SDL_MOUSEBUTTONDBL:
  1880. case SDL_MOUSEBUTTONDOWN:
  1881. // Dragging/clicking what?
  1882. whereCoords(event->button.x, event->button.y, &newDragMode, &newDragItem);
  1883. if (newDragMode == FRAME_DRAG_CLIENT) {
  1884. // No focus or drag on wheels
  1885. if ((event->button.button != SDL_BUTTON_WHEELUP) &&
  1886. (event->button.button != SDL_BUTTON_WHEELDOWN)) {
  1887. dragMode = newDragMode;
  1888. dragItem = newDragItem;
  1889. focusToolPanel(dragItem);
  1890. }
  1891. // Adjust coordinates to 0-based for client
  1892. if (clientArea[newDragItem].window) {
  1893. clientEvent.button.x -= clientArea[newDragItem].x + (newDragItem == 0 ? scrollX : 0);
  1894. clientEvent.button.y -= clientArea[newDragItem].y + (newDragItem == 0 ? scrollY : 0);
  1895. // Propogate to client
  1896. if (!clientArea[newDragItem].window->event(hasFocus && (subFocus == newDragItem), &clientEvent)) {
  1897. // If not used, handle mouse wheel ourselves
  1898. if ((newDragItem == 0) && (clientActualHeight > clientHeightInner)) {
  1899. if (event->button.button == SDL_BUTTON_WHEELUP) {
  1900. scrollTo(scrollX, scrollY + vScrollPage);
  1901. }
  1902. else if (event->button.button == SDL_BUTTON_WHEELDOWN) {
  1903. scrollTo(scrollX, scrollY - vScrollPage);
  1904. }
  1905. }
  1906. }
  1907. }
  1908. }
  1909. else if ((event->type == SDL_MOUSEBUTTONDBL) && (newDragMode == FRAME_DRAG_TITLE)) {
  1910. if ((state == FRAME_STATE_MINIMIZED) || (state == FRAME_STATE_MAXIMIZED)) restore();
  1911. else minimize();
  1912. }
  1913. else if (((event->button.button == SDL_BUTTON_WHEELUP) || (event->button.button == SDL_BUTTON_WHEELDOWN)) &&
  1914. ((newDragMode == FRAME_DRAG_SCROLLBAR_VERT) || (newDragMode == FRAME_DRAG_SCROLLBAR_HORIZ))) {
  1915. if (newDragMode == FRAME_DRAG_SCROLLBAR_VERT) {
  1916. setDirty();
  1917. whyDirty |= FRAME_DIRTY_SCROLLVERT;
  1918. if (event->button.button == SDL_BUTTON_WHEELUP) scrollTo(scrollX, scrollY + vScrollPage);
  1919. else if (event->button.button == SDL_BUTTON_WHEELDOWN) scrollTo(scrollX, scrollY - vScrollPage);
  1920. }
  1921. if (newDragMode == FRAME_DRAG_SCROLLBAR_HORIZ) {
  1922. setDirty();
  1923. whyDirty |= FRAME_DIRTY_SCROLLHORIZ;
  1924. if (event->button.button == SDL_BUTTON_WHEELUP) scrollTo(scrollX + hScrollPage, scrollY);
  1925. else if (event->button.button == SDL_BUTTON_WHEELDOWN) scrollTo(scrollX - hScrollPage, scrollY);
  1926. }
  1927. }
  1928. else if (event->button.button == SDL_BUTTON_LEFT) {
  1929. if (newDragMode == FRAME_DRAG_SCROLLBAR_VERT) {
  1930. setDirty();
  1931. whyDirty |= FRAME_DIRTY_SCROLLVERT;
  1932. scrollRepeatTicks = SDL_GetTicks();
  1933. scrollRepeatDelay = DELAY_SCROLLBAR_REPEATSTART;
  1934. if (newDragItem == FRAME_SCROLL_LESS) scrollTo(scrollX, scrollY + vScrollLine);
  1935. else if (newDragItem == FRAME_SCROLL_MORE) scrollTo(scrollX, scrollY - vScrollLine);
  1936. else if (newDragItem == FRAME_SCROLL_PAGELESS) scrollTo(scrollX, scrollY + vScrollPage);
  1937. else if (newDragItem == FRAME_SCROLL_PAGEMORE) scrollTo(scrollX, scrollY - vScrollPage);
  1938. // Force mouse motion to determine new drag mode
  1939. SDL_GetMouseState(&mX, &mY);
  1940. SDL_WarpMouse(mX, mY);
  1941. }
  1942. if (newDragMode == FRAME_DRAG_SCROLLBAR_HORIZ) {
  1943. setDirty();
  1944. whyDirty |= FRAME_DIRTY_SCROLLHORIZ;
  1945. scrollRepeatTicks = SDL_GetTicks();
  1946. scrollRepeatDelay = DELAY_SCROLLBAR_REPEATSTART;
  1947. if (newDragItem == FRAME_SCROLL_LESS) scrollTo(scrollX + hScrollLine, scrollY);
  1948. else if (newDragItem == FRAME_SCROLL_MORE) scrollTo(scrollX - hScrollLine, scrollY);
  1949. else if (newDragItem == FRAME_SCROLL_PAGELESS) scrollTo(scrollX + hScrollPage, scrollY);
  1950. else if (newDragItem == FRAME_SCROLL_PAGEMORE) scrollTo(scrollX - hScrollPage, scrollY);
  1951. // Force mouse motion to determine new drag mode
  1952. SDL_GetMouseState(&mX, &mY);
  1953. SDL_WarpMouse(mX, mY);
  1954. }
  1955. if (newDragMode == FRAME_DRAG_BUTTONS) {
  1956. setDirty();
  1957. whyDirty |= FRAME_DIRTY_TITLE;
  1958. }
  1959. dragMode = newDragMode;
  1960. dragItem = newDragItem;
  1961. dragX = event->button.x;
  1962. dragY = event->button.y;
  1963. dragItemOverflowX = 0;
  1964. dragItemOverflowY = 0;
  1965. }
  1966. break;
  1967. case SDL_MOUSEFOCUS:
  1968. // We pay attention to MOUSEMOTION; we don't propogate MOUSEFOCUS
  1969. // gain but we must alert client to loss
  1970. if ((hoverMode == FRAME_DRAG_CLIENT) && (clientArea[hoverItem].window)) {
  1971. // Lose focus
  1972. SDL_Event customEvent;
  1973. customEvent.type = SDL_MOUSEFOCUS;
  1974. customEvent.user.code = 0;
  1975. customEvent.user.data1 = NULL;
  1976. customEvent.user.data2 = NULL;
  1977. clientArea[hoverItem].window->event(hasFocus && (subFocus == hoverItem), &customEvent);
  1978. }
  1979. hoverMode = FRAME_DRAG_NONE;
  1980. break;
  1981. case SDL_MOUSEMOTION:
  1982. if (dragMode == FRAME_DRAG_CLIENT) {
  1983. // Adjust coordinates to 0-based for client
  1984. if (clientArea[dragItem].window) {
  1985. clientEvent.motion.x -= clientArea[dragItem].x + (dragItem == 0 ? scrollX : 0);
  1986. clientEvent.motion.y -= clientArea[dragItem].y + (dragItem == 0 ? scrollY : 0);
  1987. // Propogate to client
  1988. clientArea[dragItem].window->event(hasFocus && (subFocus == dragItem), &clientEvent);
  1989. }
  1990. }
  1991. else if (event->motion.state & SDL_BUTTON_LMASK) {
  1992. // These are only used if dragging within desktop
  1993. int clipX, clipY, clipW, clipH;
  1994. if (windowSort() >= WINDOWSORT_MODAL) {
  1995. clipX = 0;
  1996. clipY = 0;
  1997. clipW = screenWidth;
  1998. clipH = screenHeight;
  1999. }
  2000. else {
  2001. clipX = desktop->desktopX();
  2002. clipY = desktop->desktopY();
  2003. clipW = desktop->desktopX() + desktop->desktopWidth();
  2004. clipH = desktop->desktopY() + desktop->desktopHeight();
  2005. }
  2006. // Drag title?
  2007. // (no moving when maximized)
  2008. if ((dragMode == FRAME_DRAG_TITLE) && (state != FRAME_STATE_MAXIMIZED)) {
  2009. if (parent == desktop) {
  2010. int newX = x + event->motion.xrel;
  2011. int newY = y + event->motion.yrel;
  2012. // Overflow
  2013. if (dragItemOverflowY) {
  2014. newY += dragItemOverflowY;
  2015. dragItemOverflowY = 0;
  2016. }
  2017. if (dragItemOverflowX) {
  2018. newX += dragItemOverflowX;
  2019. dragItemOverflowX = 0;
  2020. }
  2021. if (newY + height > clipH) {
  2022. dragItemOverflowY = newY + height - clipH;
  2023. newY -= dragItemOverflowY;
  2024. }
  2025. if (newY < clipY) {
  2026. dragItemOverflowY = newY - clipY;
  2027. newY = clipY;
  2028. }
  2029. if (newX + width > clipW) {
  2030. dragItemOverflowX = newX + width - clipW;
  2031. newX -= dragItemOverflowX;
  2032. }
  2033. if (newX < clipX) {
  2034. dragItemOverflowX = newX - clipX;
  2035. newX = clipX;
  2036. }
  2037. move(newX, newY);
  2038. if (framePanel) {
  2039. int index = framePanel->findToolPanel(this);
  2040. if (index) framePanel->dragFloatPanel(index, 0);
  2041. }
  2042. }
  2043. else if (framePanel) {
  2044. int index = framePanel->findToolPanel(this);
  2045. if (index) framePanel->dragToolPanel(index, event->motion.xrel, event->motion.yrel, dragItemOverflowX, dragItemOverflowY);
  2046. }
  2047. }
  2048. // Tab?
  2049. else if ((dragMode == FRAME_DRAG_SCROLLBAR_HORIZ) &&
  2050. (dragItem == FRAME_SCROLL_TAB)) {
  2051. int newX = hScrollbarTab + event->motion.xrel;
  2052. // Overflow
  2053. if (dragItemOverflowX) {
  2054. newX += dragItemOverflowX;
  2055. dragItemOverflowX = 0;
  2056. }
  2057. if (newX + hScrollbarTabSize > (hScrollbarWidth - scrollbarButtonSize * 2)) {
  2058. dragItemOverflowX = newX + hScrollbarTabSize - (hScrollbarWidth - scrollbarButtonSize * 2);
  2059. newX -= dragItemOverflowX;
  2060. }
  2061. if (newX < 0) {
  2062. dragItemOverflowX = newX;
  2063. newX = 0;
  2064. }
  2065. // Scroll to right spot
  2066. scrollTo(newX * (clientWidthInner - clientActualWidth) / (hScrollbarWidth - (scrollbarButtonSize * 2) - hScrollbarTabSize), scrollY, newX, -1);
  2067. }
  2068. else if ((dragMode == FRAME_DRAG_SCROLLBAR_VERT) &&
  2069. (dragItem == FRAME_SCROLL_TAB)) {
  2070. int newY = vScrollbarTab + event->motion.yrel;
  2071. // Overflow
  2072. if (dragItemOverflowY) {
  2073. newY += dragItemOverflowY;
  2074. dragItemOverflowY = 0;
  2075. }
  2076. if (newY + vScrollbarTabSize > (vScrollbarHeight - scrollbarButtonSize * 2)) {
  2077. dragItemOverflowY = newY + vScrollbarTabSize - (vScrollbarHeight - scrollbarButtonSize * 2);
  2078. newY -= dragItemOverflowY;
  2079. }
  2080. if (newY < 0) {
  2081. dragItemOverflowY = newY;
  2082. newY = 0;
  2083. }
  2084. // Scroll to right spot
  2085. scrollTo(scrollX, newY * (clientHeightInner - clientActualHeight) / (vScrollbarHeight - (scrollbarButtonSize * 2) - vScrollbarTabSize), -1, newY);
  2086. }
  2087. // Buttons or scrollbar?
  2088. else if ((dragMode == FRAME_DRAG_BUTTONS) ||
  2089. (dragMode == FRAME_DRAG_SCROLLBAR_HORIZ) ||
  2090. (dragMode == FRAME_DRAG_SCROLLBAR_VERT)) {
  2091. whereCoords(event->motion.x, event->motion.y, &newDragMode, &newDragItem);
  2092. if (dragMode == newDragMode) {
  2093. if (dragItem != newDragItem) {
  2094. dragItem = newDragItem;
  2095. setDirty();
  2096. if (dragMode == FRAME_DRAG_BUTTONS) whyDirty |= FRAME_DIRTY_TITLE;
  2097. else if (dragMode == FRAME_DRAG_SCROLLBAR_HORIZ) whyDirty |= FRAME_DIRTY_SCROLLHORIZ;
  2098. else whyDirty |= FRAME_DIRTY_SCROLLVERT;
  2099. }
  2100. }
  2101. else {
  2102. if (dragItem != FRAME_RESIZE_NONE) {
  2103. dragItem = FRAME_RESIZE_NONE;
  2104. setDirty();
  2105. if (dragMode == FRAME_DRAG_BUTTONS) whyDirty |= FRAME_DIRTY_TITLE;
  2106. else if (dragMode == FRAME_DRAG_SCROLLBAR_HORIZ) whyDirty |= FRAME_DIRTY_SCROLLHORIZ;
  2107. else whyDirty |= FRAME_DIRTY_SCROLLVERT;
  2108. }
  2109. }
  2110. }
  2111. // Resize?
  2112. else if (dragMode == FRAME_DRAG_BORDER) {
  2113. if (parent == desktop) {
  2114. int newHeight = height;
  2115. int newWidth = width;
  2116. int newX = x;
  2117. int newY = y;
  2118. // 2 = titlebar inner border (1) times two
  2119. int minWidth = outerBorder * 2 + 2 + titlebarButtonTotalWidth;
  2120. int minHeight = outerBorder * 2 + FRAME_INNERBORDER * 2 + titlebarHeight;
  2121. // Only apply changes as appropriate
  2122. // Bottom
  2123. if (dragItem & FRAME_RESIZE_BOTTOM) {
  2124. newHeight += event->motion.yrel;
  2125. }
  2126. // Top
  2127. if (dragItem & FRAME_RESIZE_TOP) {
  2128. newY = y + event->motion.yrel;
  2129. newHeight -= event->motion.yrel;
  2130. }
  2131. // Overflow
  2132. if (dragItemOverflowY) {
  2133. newHeight += dragItemOverflowY;
  2134. if (dragItem & FRAME_RESIZE_TOP) newY -= dragItemOverflowY;
  2135. dragItemOverflowY = 0;
  2136. }
  2137. if (newHeight < minHeight) {
  2138. dragItemOverflowY = newHeight - minHeight;
  2139. if (dragItem & FRAME_RESIZE_TOP) newY += dragItemOverflowY;
  2140. newHeight = minHeight;
  2141. }
  2142. if (newY + newHeight > clipH) {
  2143. dragItemOverflowY = newY + newHeight - clipH;
  2144. newHeight -= dragItemOverflowY;
  2145. }
  2146. if (newY < clipY) {
  2147. dragItemOverflowY = clipY - newY;
  2148. newHeight -= dragItemOverflowY;
  2149. newY = clipY;
  2150. }
  2151. // Right
  2152. if (dragItem & FRAME_RESIZE_RIGHT) {
  2153. newWidth += event->motion.xrel;
  2154. }
  2155. // Left
  2156. if (dragItem & FRAME_RESIZE_LEFT) {
  2157. newX = x + event->motion.xrel;
  2158. newWidth -= event->motion.xrel;
  2159. }
  2160. // Overflow
  2161. if (dragItemOverflowX) {
  2162. newWidth += dragItemOverflowX;
  2163. if (dragItem & FRAME_RESIZE_LEFT) newX -= dragItemOverflowX;
  2164. dragItemOverflowX = 0;
  2165. }
  2166. if (newWidth < minWidth) {
  2167. dragItemOverflowX = newWidth - minWidth;
  2168. if (dragItem & FRAME_RESIZE_LEFT) newX += dragItemOverflowX;
  2169. newWidth = minWidth;
  2170. }
  2171. if (newX + newWidth > clipW) {
  2172. dragItemOverflowX = newX + newWidth - clipW;
  2173. newWidth -= dragItemOverflowX;
  2174. }
  2175. if (newX < clipX) {
  2176. dragItemOverflowX = clipX - newX;
  2177. newWidth -= dragItemOverflowX;
  2178. newX = clipX;
  2179. }
  2180. move(newX, newY);
  2181. resize(newWidth, newHeight);
  2182. // If we didn't reach target size, "undo" some
  2183. if ((width != newWidth) || (height != newHeight)) {
  2184. if (width != newWidth) {
  2185. int offBy = newWidth - width;
  2186. if (dragItem & FRAME_RESIZE_LEFT) newX += offBy;
  2187. newWidth -= offBy;
  2188. dragItemOverflowX += offBy;
  2189. }
  2190. if (height != newHeight) {
  2191. int offBy = newHeight - height;
  2192. if (dragItem & FRAME_RESIZE_TOP) newY += offBy;
  2193. newHeight -= offBy;
  2194. dragItemOverflowY += offBy;
  2195. }
  2196. move(newX, newY);
  2197. resize(newWidth, newHeight);
  2198. }
  2199. setDirty();
  2200. whyDirty = FRAME_DIRTY_ALL;
  2201. }
  2202. else {
  2203. // @TODO: resize within other window
  2204. }
  2205. }
  2206. }
  2207. // Just hover?
  2208. else {
  2209. doMouseHover(event->motion.x, event->motion.y, hasFocus, clientEvent);
  2210. }
  2211. break;
  2212. case SDL_INPUTFOCUS:
  2213. if ((event->user.code & 1) && (!haveFocus)) {
  2214. haveFocus = 1;
  2215. setDirty();
  2216. whyDirty |= FRAME_DIRTY_TITLE;
  2217. // Propogate to client
  2218. for (pos = 0; pos < (int)clientArea.size(); ++pos) {
  2219. if (clientArea[pos].window) {
  2220. clientEvent.user.code = pos == subFocus ? 1 : 2;
  2221. clientArea[pos].window->event(hasFocus, &clientEvent);
  2222. }
  2223. }
  2224. }
  2225. else if (!(event->user.code & 1) && (haveFocus)) {
  2226. haveFocus = 0;
  2227. dragMode = FRAME_DRAG_NONE;
  2228. setDirty();
  2229. whyDirty |= FRAME_DIRTY_TITLE;
  2230. // Propogate to client
  2231. for (pos = 0; pos < (int)clientArea.size(); ++pos) {
  2232. if (clientArea[pos].window) {
  2233. clientArea[pos].window->event(hasFocus, &clientEvent);
  2234. }
  2235. }
  2236. }
  2237. break;
  2238. case SDL_KEYDOWN:
  2239. case SDL_KEYUP:
  2240. // Propogate to client- current focus area only
  2241. if (clientArea[subFocus].window) {
  2242. if (clientArea[subFocus].window->event(hasFocus, &clientEvent)) {
  2243. return 1;
  2244. }
  2245. }
  2246. // If current focus is tool panel or bar, give main area a shot at it
  2247. if ((subFocus != 0) && (clientArea[0].window)) {
  2248. // Don't do this for modifier keys
  2249. if ((clientEvent.key.keysym.sym != SDLK_LSHIFT) &&
  2250. (clientEvent.key.keysym.sym != SDLK_RSHIFT) &&
  2251. (clientEvent.key.keysym.sym != SDLK_LALT) &&
  2252. (clientEvent.key.keysym.sym != SDLK_RALT) &&
  2253. (clientEvent.key.keysym.sym != SDLK_LCTRL) &&
  2254. (clientEvent.key.keysym.sym != SDLK_RCTRL)) {
  2255. if (clientArea[0].window->event(hasFocus, &clientEvent)) {
  2256. // Switch focus to it
  2257. focusToolPanel(0);
  2258. return 1;
  2259. }
  2260. }
  2261. }
  2262. return 0;
  2263. case SDL_COMMAND:
  2264. // Catch certain commands
  2265. switch (event->user.code) {
  2266. case WINDOW_NEXT:
  2267. nextWindow();
  2268. return 1;
  2269. case WINDOW_PREV:
  2270. prevWindow();
  2271. return 1;
  2272. case WINDOW_CLOSE:
  2273. closeWindow(); // Calls attemptClose
  2274. return 1;
  2275. case WINDOW_MIN:
  2276. if (state == FRAME_STATE_MINIMIZED) restore();
  2277. else minimize();
  2278. return 1;
  2279. case WINDOW_MAX:
  2280. if (state == FRAME_STATE_MAXIMIZED) restore();
  2281. else maximize();
  2282. return 1;
  2283. case WINDOW_CASCADE:
  2284. // (Reset cascade position)
  2285. cascadeStep = -1;
  2286. case WINDOW_CLOSEALL:
  2287. case WINDOW_CLOSEALLBUT:
  2288. case WINDOW_TILEHORIZ:
  2289. case WINDOW_TILEVERT: {
  2290. Window* nextWin = NULL;
  2291. int count = 0;
  2292. int cascadeOffset = outerBorder + titlebarHeight;
  2293. int cascadeWidth = desktop->desktopWidth() - cascadeOffset * 3;
  2294. int cascadeHeight = desktop->desktopHeight() - cascadeOffset * 3;
  2295. // First pass- does closes/cascades
  2296. // (assignment intentional)
  2297. while ( (nextWin = desktop->findWindow(WINDOW_FRAME, count)) ) {
  2298. ++count;
  2299. FrameWindow* fwin = dynamic_cast<FrameWindow*>(nextWin);
  2300. if (fwin) {
  2301. if ((event->user.code == WINDOW_CLOSEALL) ||
  2302. ((event->user.code == WINDOW_CLOSEALLBUT) && (fwin != this))) {
  2303. fwin->closeWindow();
  2304. }
  2305. else if (event->user.code == WINDOW_CASCADE) {
  2306. fwin->restore();
  2307. if (++cascadeStep > 3) cascadeStep = 0;
  2308. fwin->move(desktop->desktopX() + cascadeStep * cascadeOffset,
  2309. desktop->desktopY() + cascadeStep * cascadeOffset);
  2310. fwin->resize(cascadeWidth, cascadeHeight);
  2311. }
  2312. }
  2313. }
  2314. // Do another pass for tiling
  2315. if ((event->user.code == WINDOW_TILEHORIZ) || (event->user.code == WINDOW_TILEVERT)) {
  2316. // Calculate minimum rows/columns to hold 'count' windows
  2317. // Special exception- 3 or less
  2318. int rows = 1, cols = 1;
  2319. if (count <= 3) rows = count;
  2320. else {
  2321. while (rows * cols < count) {
  2322. if (rows == cols) ++rows;
  2323. else ++cols;
  2324. }
  2325. }
  2326. // Number of entries in each row (horiz) or col (vert)
  2327. int* entries = new int[rows];
  2328. memset(entries, 0, sizeof(int[rows]));
  2329. // Fill up the rows, from right-to-left (bottom-to-top)
  2330. int row = rows;
  2331. for (int pos = 0; pos < count; ++pos) {
  2332. ++entries[--row];
  2333. if (row == 0) row = rows;
  2334. }
  2335. // Now we move windows, fill last spot first
  2336. int currRow = rows - 1;
  2337. int currCol = entries[currRow] - 1;
  2338. int pos = 0;
  2339. // (assignment intentional)
  2340. while ( (nextWin = desktop->findWindow(WINDOW_FRAME, pos++)) ) {
  2341. FrameWindow* fwin = dynamic_cast<FrameWindow*>(nextWin);
  2342. if (fwin) {
  2343. fwin->restore();
  2344. int width, height;
  2345. if (event->user.code == WINDOW_TILEVERT) {
  2346. // row/width are swapped
  2347. width = desktop->desktopWidth() / rows;
  2348. height = desktop->desktopHeight() / entries[currRow];
  2349. fwin->move(desktop->desktopX() + width * currRow,
  2350. desktop->desktopY() + height * currCol);
  2351. }
  2352. else {
  2353. width = desktop->desktopWidth() / entries[currRow];
  2354. height = desktop->desktopHeight() / rows;
  2355. fwin->move(desktop->desktopX() + width * currCol,
  2356. desktop->desktopY() + height * currRow);
  2357. }
  2358. fwin->resize(width, height);
  2359. }
  2360. // Next spot
  2361. if (--currCol < 0) {
  2362. --currRow;
  2363. currCol = entries[currRow] - 1;
  2364. }
  2365. }
  2366. delete[] entries;
  2367. }
  2368. return 1;
  2369. }
  2370. case WINDOW_SWITCHPANEL:
  2371. // @TODO:
  2372. /*
  2373. if ((subFocus == 0) && (clientArea[1])) {
  2374. // If not open, open it- this switches focus too
  2375. if (!toolPanelOpen) toggleToolPanel();
  2376. else focusToolPanel(1);
  2377. return 1;
  2378. }
  2379. if ((subFocus < 2) && (clientArea[2])) {
  2380. if (!toolBarOpen) toggleToolBar();
  2381. else focusToolPanel(2);
  2382. return 1;
  2383. }
  2384. focusToolPanel(0);
  2385. */
  2386. return 1;
  2387. case VIEW_TOOLPANEL:
  2388. // @TODO:
  2389. /*
  2390. toggleToolPanel();
  2391. */
  2392. return 1;
  2393. case VIEW_TOOLBAR:
  2394. // @TODO:
  2395. /*
  2396. toggleToolBar();
  2397. */
  2398. return 1;
  2399. }
  2400. // Propogate to client- try current panel, then others
  2401. if ((clientArea[subFocus].window) && (exempt != clientArea[subFocus].window)) {
  2402. if (clientArea[subFocus].window->event(hasFocus, &clientEvent)) return 1;
  2403. }
  2404. for (pos = 0; pos < (int)clientArea.size(); ++pos) {
  2405. if (pos != subFocus) {
  2406. if ((clientArea[pos].window) && (exempt != clientArea[pos].window)) {
  2407. if (clientArea[pos].window->event(0, &clientEvent)) return 1;
  2408. }
  2409. }
  2410. }
  2411. return 0;
  2412. case SDL_SPECIAL:
  2413. // Scrollbar repeat?
  2414. if ((event->user.code & SDL_IDLE) && (scrollRepeatDelay)) {
  2415. ticks = SDL_GetTicks();
  2416. // (catch wraparound)
  2417. if (ticks < scrollRepeatTicks) scrollRepeatTicks = SDL_GetTicks();
  2418. else if (ticks - scrollRepeatTicks > scrollRepeatDelay) {
  2419. scrollRepeatTicks = SDL_GetTicks();
  2420. scrollRepeatDelay = DELAY_SCROLLBAR_REPEATAGAIN;
  2421. if (dragMode == FRAME_DRAG_SCROLLBAR_VERT) {
  2422. if (dragItem == FRAME_SCROLL_LESS) scrollTo(scrollX, scrollY + vScrollLine);
  2423. else if (dragItem == FRAME_SCROLL_MORE) scrollTo(scrollX, scrollY - vScrollLine);
  2424. else if (dragItem == FRAME_SCROLL_PAGELESS) scrollTo(scrollX, scrollY + vScrollPage);
  2425. else if (dragItem == FRAME_SCROLL_PAGEMORE) scrollTo(scrollX, scrollY - vScrollPage);
  2426. }
  2427. if (dragMode == FRAME_DRAG_SCROLLBAR_HORIZ) {
  2428. if (dragItem == FRAME_SCROLL_LESS) scrollTo(scrollX + hScrollLine, scrollY);
  2429. else if (dragItem == FRAME_SCROLL_MORE) scrollTo(scrollX - hScrollLine, scrollY);
  2430. else if (dragItem == FRAME_SCROLL_PAGELESS) scrollTo(scrollX + hScrollPage, scrollY);
  2431. else if (dragItem == FRAME_SCROLL_PAGEMORE) scrollTo(scrollX - hScrollPage, scrollY);
  2432. }
  2433. // Force mouse motion to determine new drag mode
  2434. SDL_GetMouseState(&mX, &mY);
  2435. SDL_WarpMouse(mX, mY);
  2436. used = 1;
  2437. }
  2438. }
  2439. // (continue on, to propogate to client)
  2440. case SDL_QUIT:
  2441. case SDL_SYSKEY:
  2442. case SDL_OBJECTCHANGE:
  2443. // (exempt window may exist)
  2444. exempt = (Window*)clientEvent.user.data2;
  2445. default:
  2446. // Propogate to client
  2447. for (pos = 0; pos < (int)clientArea.size(); ++pos) {
  2448. if ((clientArea[pos].window) && (exempt != clientArea[pos].window)) {
  2449. used = clientArea[pos].window->event(hasFocus && (subFocus == pos), &clientEvent) || used;
  2450. }
  2451. }
  2452. return used;
  2453. }
  2454. // All mouse and focus events, we use.
  2455. return 1;
  2456. }
  2457. void FrameWindow::doMouseHover(int x, int y, int hasFocus, SDL_Event& clientEvent) { start_func
  2458. DragType newDragMode;
  2459. DragSubType newDragItem;
  2460. whereCoords(x, y, &newDragMode, &newDragItem);
  2461. if (newDragMode == FRAME_DRAG_BORDER) {
  2462. if ((newDragItem == FRAME_RESIZE_BOTTOM) || (newDragItem == FRAME_RESIZE_TOP))
  2463. selectMouse(MOUSE_VERT);
  2464. else if ((newDragItem == FRAME_RESIZE_LEFT) || (newDragItem == FRAME_RESIZE_RIGHT))
  2465. selectMouse(MOUSE_HORIZ);
  2466. else if ((newDragItem == (FRAME_RESIZE_LEFT | FRAME_RESIZE_BOTTOM)) || (newDragItem == (FRAME_RESIZE_RIGHT | FRAME_RESIZE_TOP)))
  2467. selectMouse(MOUSE_DIAGUP);
  2468. else if ((newDragItem == (FRAME_RESIZE_LEFT | FRAME_RESIZE_TOP)) || (newDragItem == (FRAME_RESIZE_RIGHT | FRAME_RESIZE_BOTTOM)))
  2469. selectMouse(MOUSE_DIAGDOWN);
  2470. }
  2471. else if (newDragMode == FRAME_DRAG_TITLE) selectMouse(MOUSE_FOURDIRECTION);
  2472. else if ((newDragMode != hoverMode) || (newDragItem != hoverItem)) selectMouse(MOUSE_NORMAL);
  2473. if ((hoverMode == FRAME_DRAG_CLIENT) && (clientArea[hoverItem].window) && ((newDragMode != hoverMode) || (newDragItem != hoverItem))) {
  2474. // Lose focus
  2475. SDL_Event customEvent;
  2476. customEvent.type = SDL_MOUSEFOCUS;
  2477. customEvent.user.code = 0;
  2478. customEvent.user.data1 = NULL;
  2479. customEvent.user.data2 = NULL;
  2480. clientArea[hoverItem].window->event(hasFocus && (subFocus == hoverItem), &customEvent);
  2481. }
  2482. if (newDragMode == FRAME_DRAG_CLIENT) {
  2483. if (clientArea[newDragItem].window) {
  2484. // Gain focus?
  2485. if ((newDragMode != hoverMode) || (newDragItem != hoverItem)) {
  2486. SDL_Event customEvent;
  2487. customEvent.type = SDL_MOUSEFOCUS;
  2488. customEvent.user.code = 1;
  2489. customEvent.user.data1 = NULL;
  2490. customEvent.user.data2 = NULL;
  2491. clientArea[newDragItem].window->event(hasFocus && (subFocus == newDragItem), &customEvent);
  2492. // Force to be a mouse motion event if gaining focus
  2493. if (clientEvent.type == SDL_MOUSEBUTTONUP) {
  2494. int x = clientEvent.button.x;
  2495. int y = clientEvent.button.y;
  2496. clientEvent.type = SDL_MOUSEMOTION;
  2497. clientEvent.motion.x = x;
  2498. clientEvent.motion.y = y;
  2499. clientEvent.motion.state = SDL_GetMouseState(NULL, NULL);
  2500. clientEvent.motion.xrel = 0;
  2501. clientEvent.motion.yrel = 0;
  2502. }
  2503. }
  2504. // Only propogate hovering- MouseUp was alredy done
  2505. if (clientEvent.type != SDL_MOUSEBUTTONUP) {
  2506. // Adjust coordinates to 0-based for client
  2507. if (clientEvent.type == SDL_MOUSEMOTION) {
  2508. clientEvent.motion.x -= clientArea[newDragItem].x + (newDragItem == 0 ? scrollX : 0);
  2509. clientEvent.motion.y -= clientArea[newDragItem].y + (newDragItem == 0 ? scrollY : 0);
  2510. }
  2511. else {
  2512. clientEvent.button.x -= clientArea[newDragItem].x + (newDragItem == 0 ? scrollX : 0);
  2513. clientEvent.button.y -= clientArea[newDragItem].y + (newDragItem == 0 ? scrollY : 0);
  2514. }
  2515. // Propogate to client
  2516. clientArea[newDragItem].window->event(hasFocus && (subFocus == newDragItem), &clientEvent);
  2517. }
  2518. }
  2519. }
  2520. hoverMode = newDragMode;
  2521. hoverItem = newDragItem;
  2522. }