motifEmulator.cpp 101 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678
  1. /* motifEmulator.cpp
  2. *
  3. * Copyright (C) 1993-2011,2012,2015,2016,2017 Paul Boersma
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <stdarg.h>
  21. #include <string.h>
  22. #include <ctype.h>
  23. #include <time.h>
  24. #include "melder.h"
  25. #include "GuiP.h"
  26. #include "machine.h"
  27. static void (*theOpenDocumentCallback) (MelderFile file);
  28. static int (*theQuitApplicationCallback) ();
  29. #if defined (_WIN32)
  30. /* The Motif emulator for Macintosh and Windows. */
  31. #define PRAAT_WINDOW_CLASS_NUMBER 1
  32. #define SCROLL32 1
  33. /*
  34. * The MEMBER macros only work if klas1 etc are no macros themselves.
  35. * Therefore, we undefine those who are:
  36. */
  37. #undef MessageBox
  38. #define _motif_SHIFT_MASK 1
  39. #define _motif_COMMAND_MASK 2
  40. #define _motif_OPTION_MASK 4
  41. #define CELL_HEIGHT 15
  42. #define MESSAGE_BOX_BUTTON_WIDTH 100
  43. static void _motif_update (GuiObject me, void *event);
  44. /********** Resource names. **********/
  45. #define motif_RESOURCE(xxx) #xxx,
  46. static const char *motif_resourceNames [] = {
  47. "XmNnull",
  48. #include "motifEmulator_resources.h"
  49. "XmNend"
  50. };
  51. #undef motif_RESOURCE
  52. /* Modes. */
  53. struct Gui theGui; // global variable
  54. /********** XWindows routines. **********/
  55. void XMapRaised (int displayDummy, Window window) {
  56. (void) displayDummy;
  57. ShowWindow ((HWND) window, SW_SHOW);
  58. SetForegroundWindow ((HWND) window);
  59. }
  60. int WidthOfScreen (int screen) {
  61. (void) screen;
  62. return GetSystemMetrics (SM_CXMAXIMIZED);
  63. }
  64. int HeightOfScreen (int screen) {
  65. (void) screen;
  66. return GetSystemMetrics (SM_CYMAXIMIZED);
  67. }
  68. /********** X Toolkit **********/
  69. void _Gui_callCallbacks (GuiObject w, XtCallbackList *callbacks, XtPointer call) {
  70. int i; for (i = 0; i < MAXNUM_CALLBACKS; i ++)
  71. if (callbacks -> pairs [i]. proc) callbacks -> pairs [i]. proc (w, callbacks -> pairs [i]. closure, call);
  72. }
  73. /* When dispatching events to widgets, we must translate from the identifier of a Macintosh
  74. * screen object (Window, Control, menu item) to a GuiObject.
  75. * Mac windows and controls have RefCon fields in their WindowRecord or ControlRecord,
  76. * so we put a reference to the widget in the appropriate RefCon field at window or control creation time.
  77. * Instead of RefCons, the menus are remembered here:
  78. */
  79. #define MAXIMUM_NUMBER_OF_MENUS 4000
  80. static GuiObject theMenus [1+MAXIMUM_NUMBER_OF_MENUS]; // we can freely use and reuse these menu ids
  81. static char32 theApplicationName [100], theWindowClassName [100], theDrawingAreaClassName [100], theApplicationClassName [100];
  82. char32 * _GuiWin_getDrawingAreaClassName () { return theDrawingAreaClassName; }
  83. static int (*theUserMessageCallback) ();
  84. #define MINIMUM_MENU_ITEM_ID (MAXIMUM_NUMBER_OF_MENUS + 1)
  85. #define MAXIMUM_MENU_ITEM_ID 32767
  86. static short theMenuItems [1+MAXIMUM_MENU_ITEM_ID]; // we can freely use and reuse the item ids 4001..32767
  87. static GuiObject theApplicationShell; // for global menus
  88. static int theBackground = False; // set by suspend and resume events; used by Motif-style activation methods
  89. static int theDialogHint = False; // should the shell that is currently being created, have dialog or document looks?
  90. integer numberOfWidgets = 0;
  91. integer Gui_getNumberOfMotifWidgets () { return numberOfWidgets; }
  92. /* AppContext level */
  93. static int theNumberOfWorkProcs;
  94. static XtWorkProc theWorkProcs [10];
  95. static XtPointer theWorkProcClosures [10];
  96. static int theNumberOfTimeOuts;
  97. static XtTimerCallbackProc theTimeOutProcs [10];
  98. static XtPointer theTimeOutClosures [10];
  99. static clock_t theTimeOutStarts [10];
  100. static uinteger theTimeOutIntervals [10];
  101. static void Native_move (GuiObject w, int dx, int dy); // forward
  102. static void cb_scroll (GuiObject scrollBar, XtPointer closure, XtPointer call) {
  103. GuiObject scrolledWindow = (GuiObject) closure;
  104. GuiObject workWindow = scrolledWindow -> motiff.scrolledWindow.workWindow;
  105. int previousShift, newShift, distance;
  106. int horizontal = scrolledWindow -> motiff.scrolledWindow.horizontalBar == scrollBar;
  107. (void) call;
  108. if (! workWindow) return;
  109. Melder_assert (scrolledWindow -> motiff.scrolledWindow.clipWindow);
  110. previousShift = horizontal ?
  111. scrolledWindow -> motiff.scrolledWindow.clipWindow -> x - workWindow -> x :
  112. scrolledWindow -> motiff.scrolledWindow.clipWindow -> y - workWindow -> y;
  113. newShift = scrollBar -> value;
  114. distance = previousShift - newShift;
  115. if (horizontal)
  116. { workWindow -> x += distance; Native_move (workWindow, distance, 0); }
  117. else
  118. { workWindow -> y += distance; Native_move (workWindow, 0, distance); }
  119. GdiFlush (); // for dragging
  120. }
  121. /* These are like the toolkit's query_geometry methods: */
  122. static int Native_titleWidth (GuiObject me) {
  123. if (my parent -> window) {
  124. HDC dc = GetDC (my parent -> window);
  125. SIZE size;
  126. SelectFont (dc, GetStockFont (ANSI_VAR_FONT)); // possible BUG
  127. conststringW nameW = Melder_peek32toW (my name.get());
  128. GetTextExtentPoint32 (dc, nameW, wcslen (nameW), & size);
  129. ReleaseDC (my parent -> window, dc);
  130. return size. cx;
  131. } else {
  132. return 7 * str32len (my name.get());
  133. }
  134. }
  135. static int NativeLabel_preferredWidth (GuiObject me) {
  136. return Native_titleWidth (me) + 10;
  137. }
  138. static int NativeButton_preferredWidth (GuiObject me) {
  139. int width = Native_titleWidth (me) + 10;
  140. return width < 41 ? 41 : width;
  141. }
  142. static int NativeToggleButton_preferredWidth (GuiObject me) {
  143. return Native_titleWidth (me) + 25;
  144. }
  145. static int NativeButton_preferredHeight (GuiObject me) {
  146. (void) me;
  147. return 22;
  148. }
  149. /***** WIDGET *****/
  150. GuiObject _Gui_initializeWidget (int widgetClass, GuiObject parent, conststring32 name) {
  151. GuiObject me = Melder_calloc_f (struct structGuiObject, 1);
  152. if (Melder_debug == 34)
  153. Melder_casual (U"from _Gui_initializeWidget\t", Melder_pointer (me), U"\t1\t", sizeof (struct structGuiObject));
  154. my magicNumber = 15111959;
  155. numberOfWidgets ++;
  156. my widgetClass = widgetClass;
  157. my parent = parent;
  158. my inMenu = parent && MEMBER (parent, PulldownMenu);
  159. /*
  160. * Install me into my parent's list of children.
  161. */
  162. if (! parent) {
  163. ;
  164. } else if (! parent -> firstChild) {
  165. parent -> firstChild = me;
  166. } else {
  167. GuiObject lastChild = parent -> firstChild;
  168. while (lastChild -> nextSibling) lastChild = lastChild -> nextSibling;
  169. lastChild -> nextSibling = me;
  170. my previousSibling = lastChild;
  171. }
  172. /*
  173. * Copy the name into my name.
  174. */
  175. my name = Melder_dup_f (name);
  176. /*
  177. * I am in the same shell as my parent, so I inherit my parent's "shell" attribute.
  178. */
  179. if (MEMBER (me, Shell)) {
  180. my shell = me;
  181. } else {
  182. my shell = parent ? parent -> shell : nullptr;
  183. }
  184. /*
  185. * The remainder of initialization is about positioning, sizes, attachments, and the contents of a scrolled window.
  186. * All of that is irrelevant to menu items.
  187. */
  188. if (my inMenu) return me;
  189. /* Initial defaults: mainly positioning and sizes. */
  190. switch (my widgetClass) {
  191. case xmDrawingAreaWidgetClass: {
  192. my x = 2;
  193. my y = 2;
  194. my width = 100;
  195. my height = 100;
  196. } break; case xmShellWidgetClass: {
  197. my x = 20;
  198. my y = 3;
  199. my width = 30;
  200. my height = 50;
  201. my deleteResponse = XmDESTROY;
  202. } break; case xmTextWidgetClass: {
  203. my x = 2;
  204. my y = 2;
  205. my width = 102;
  206. my height = Gui_TEXTFIELD_HEIGHT;
  207. } break; case xmPushButtonWidgetClass: {
  208. my x = 2;
  209. my y = 2;
  210. my width = NativeButton_preferredWidth (me);
  211. my height = Gui_PUSHBUTTON_HEIGHT;
  212. } break; case xmLabelWidgetClass: {
  213. my x = 2;
  214. my y = 2;
  215. my width = NativeLabel_preferredWidth (me);
  216. my height = Gui_LABEL_HEIGHT;
  217. } break; case xmCascadeButtonWidgetClass: {
  218. if (my parent -> rowColumnType == XmMENU_BAR) {
  219. char32 *hyphen = str32str (my name.get(), U" -");
  220. if (hyphen) hyphen [2] = U'\0'; // chop any trailing spaces
  221. my x = 2;
  222. my y = 2;
  223. my width = NativeButton_preferredWidth (me);
  224. my height = NativeButton_preferredHeight (me) + 4; // BUG: menu bar should be large enough
  225. } else {
  226. my motiff.cascadeButton.inBar = true;
  227. }
  228. } break; case xmToggleButtonWidgetClass: {
  229. my x = 2;
  230. my y = 2;
  231. my width = NativeToggleButton_preferredWidth (me);
  232. my height = Gui_CHECKBUTTON_HEIGHT;
  233. } break; case xmSeparatorWidgetClass: {
  234. my width = parent -> width;
  235. my height = 10;
  236. } break; case xmScrollBarWidgetClass: {
  237. my width = 16;
  238. my height = 100;
  239. } break; case xmMenuBarWidgetClass: {
  240. my width = 10;
  241. my height = 10;
  242. } break; case xmRowColumnWidgetClass: {
  243. my width = 10;
  244. my height = 10;
  245. my orientation = XmVERTICAL;
  246. } break; case xmScaleWidgetClass: {
  247. my width = 300;
  248. my height = 25;
  249. } break; case xmFormWidgetClass: {
  250. if (MEMBER (parent, Shell)) {
  251. /*
  252. * The following trick is necessary for forms that contain scroll bars.
  253. */
  254. my width = parent -> width;
  255. my height = parent -> height;
  256. } else {
  257. /*
  258. * EXPERIMENT:
  259. * this relies on a certain sequence in building up a window: from top to bottom.
  260. * If the form is inside a RowColumn of a certain width established by a widget
  261. * closer to the top, this width will be copied. The height, of course, should not be copied.
  262. * See Praat's SoundRecorder for an example.
  263. */
  264. my width = parent -> width;
  265. my height = 10;
  266. }
  267. } break; default: {
  268. my width = parent ? parent -> width : 0;
  269. my height = parent ? parent -> height : 0;
  270. }
  271. }
  272. /* Some positions depend on parent. */
  273. if (parent) switch (parent -> widgetClass) {
  274. case xmFrameWidgetClass: {
  275. my x = 1;
  276. my y = 1;
  277. my width = parent -> width - 2;
  278. my height = parent -> height - 2;
  279. } break; case xmScrolledWindowWidgetClass: {
  280. my x = 1;
  281. my y = 1;
  282. my width = parent -> width - 17; // exact fit: scroll bar (16) plus border (1)
  283. my height = parent -> height - 17;
  284. if (my widgetClass == xmTextWidgetClass) { my width = 3000; my height = 30000; } // BUG: either put in GuiText or erase
  285. }
  286. }
  287. if (my width < 0) my width = 0;
  288. if (my height < 0) my height = 0;
  289. /* Automatic attachment of dialog to parent shell. */
  290. if (MEMBER2 (me, BulletinBoard, Form) && MEMBER (my parent, Shell))
  291. my leftAttachment = my rightAttachment = my topAttachment = my bottomAttachment = XmATTACH_FORM;
  292. if (MEMBER (me, CascadeButton) && str32equ (name, U"Help"))
  293. my rightAttachment = XmATTACH_FORM; /* !!!!!! */
  294. /* A child of a scrolled window will be installed as the workWindow of that scrolled window,
  295. * except if it is a scroll bar or if the clipWindow does not exist yet.
  296. * This is because the creations of the scroll bars and the clip window will also arrive here.
  297. * Our XmScrolledWindow creation method always creates two scroll bars and a clip window,
  298. * before you can create any other children.
  299. */
  300. if (my parent && MEMBER (my parent, ScrolledWindow) &&
  301. ! MEMBER (me, ScrollBar) && // 'me' is one of the two scroll bars, or a new one
  302. my parent -> motiff.scrolledWindow.clipWindow) // 'me' is probably the clip window now
  303. my parent -> motiff.scrolledWindow.workWindow = me; // install
  304. return me;
  305. }
  306. /***** NATIVE *****/
  307. void _GuiNativeControl_check (GuiObject me, Boolean value) {
  308. Button_SetCheck (my window, value ? BST_CHECKED : BST_UNCHECKED);
  309. }
  310. void _GuiNativeControl_destroy (GuiObject me) {
  311. DestroyWindow (my window);
  312. }
  313. void _GuiNativeControl_show (GuiObject me) {
  314. ShowWindow (my window, SW_SHOW);
  315. }
  316. void _GuiNativeControl_hide (GuiObject me) {
  317. ShowWindow (my window, SW_HIDE);
  318. }
  319. void _GuiNativeControl_setSensitive (GuiObject me) {
  320. EnableWindow (my window, ! my insensitive);
  321. }
  322. char32 * _GuiWin_expandAmpersands (conststring32 title) {
  323. static char32 buffer [300];
  324. const char32 *from = & title [0];
  325. char32 *to = & buffer [0];
  326. while (*from) { if (*from == U'&') * to ++ = U'&'; * to ++ = * from ++; } * to = U'\0';
  327. return buffer;
  328. }
  329. void _GuiNativeControl_setTitle (GuiObject me) {
  330. HDC dc = GetDC (my window);
  331. SelectPen (dc, GetStockPen (NULL_PEN));
  332. SelectBrush (dc, GetStockBrush (LTGRAY_BRUSH));
  333. Rectangle (dc, 0, 0, my width, my height);
  334. ReleaseDC (my window, dc);
  335. SetWindowTextW (my window, Melder_peek32toW (_GuiWin_expandAmpersands (my name.get())));
  336. }
  337. static int _XmScrollBar_check (GuiObject me) {
  338. if (my maximum < my minimum)
  339. Melder_warning (U"XmScrollBar: maximum (", my maximum, U") less than minimum (", my minimum, U").");
  340. else if (my sliderSize > my maximum - my minimum)
  341. Melder_warning (U"XmScrollBar: slider size (", my sliderSize, U") greater than maximum (",
  342. my maximum, U") minus minimum (", my minimum, U").");
  343. else if (my value < my minimum)
  344. Melder_warning (U"XmScrollBar: value (", my value, U") less than minimum (", my minimum, U").");
  345. else if (my value > my maximum - my sliderSize)
  346. Melder_warning (U"XmScrollBar: value (", my value, U") greater than maximum (",
  347. my maximum, U") minus slider size (", my sliderSize, U").");
  348. else return 1;
  349. return 0;
  350. }
  351. static void NativeScrollBar_set (GuiObject me) {
  352. if (! _XmScrollBar_check (me)) return;
  353. {
  354. SCROLLINFO scrollInfo;
  355. scrollInfo. cbSize = sizeof (SCROLLINFO);
  356. scrollInfo. fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
  357. #if SCROLL32
  358. if (my maximum == my minimum + my sliderSize) {
  359. scrollInfo. nMin = 0;
  360. scrollInfo. nMax = 0;
  361. scrollInfo. nPage = 1;
  362. scrollInfo. nPos = 0;
  363. } else {
  364. scrollInfo. nMin = 0;
  365. scrollInfo. nMax = 32767;
  366. scrollInfo. nPage = (32767.0 * my sliderSize) / (my maximum - my minimum);
  367. scrollInfo. nPos = ((32767.0 - scrollInfo. nPage) * (my value - my minimum)) / (my maximum - my minimum - my sliderSize);
  368. }
  369. #else
  370. scrollInfo. nMin = my minimum;
  371. scrollInfo. nMax = my maximum;
  372. scrollInfo. nPage = my sliderSize;
  373. scrollInfo. nPos = my value;
  374. #endif
  375. if (my window) SetScrollInfo (my window, SB_CTL, & scrollInfo, true);
  376. }
  377. }
  378. static void NativeMenuItem_delete (GuiObject me) {
  379. RemoveMenu (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND);
  380. }
  381. static int NativeMenuItem_getPosition (GuiObject me) {
  382. int position = 1;
  383. /*
  384. * The following routine could also be used for Mac.
  385. */
  386. GuiObject sibling;
  387. for (sibling = my parent -> firstChild; sibling; sibling = sibling -> nextSibling) {
  388. if (sibling == me) break;
  389. if (sibling -> managed && ! MEMBER (sibling, PulldownMenu))
  390. position += 1;
  391. }
  392. /*
  393. * Bill Gates counts like 0, 1, 2...
  394. */
  395. position -= 1;
  396. return position;
  397. }
  398. static void NativeMenuItem_check (GuiObject me, Boolean value) {
  399. if (! my managed) return;
  400. CheckMenuItem (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND | ( value ? MF_CHECKED : MF_UNCHECKED ));
  401. }
  402. static void NativeMenuItem_setSensitive (GuiObject me) {
  403. if (! my managed) return;
  404. EnableMenuItem (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND | ( my insensitive ? MF_GRAYED : MF_ENABLED ));
  405. //DrawMenuBar (my shell -> window);
  406. }
  407. static void NativeMenuItem_setText (GuiObject me) {
  408. int acc = my motiff.pushButton.acceleratorChar, modifiers = my motiff.pushButton.acceleratorModifiers;
  409. static MelderString title { };
  410. if (acc == 0) {
  411. MelderString_copy (& title, _GuiWin_expandAmpersands (my name.get()));
  412. } else {
  413. static const conststring32 keyStrings [256] = {
  414. 0, U"<-", U"->", U"Up", U"Down", U"PAUSE", U"Del", U"Ins", U"Backspace", U"Tab", U"LineFeed", U"Home", U"End", U"Enter", U"PageUp", U"PageDown",
  415. U"Esc", U"F1", U"F2", U"F3", U"F4", U"F5", U"F6", U"F7", U"F8", U"F9", U"F10", U"F11", U"F12", 0, 0, 0,
  416. U"Space", U"!", U"\"", U"#", U"$", U"%", U"&", U"\'", U"(", U")", U"*", U"+", U",", U"-", U".", U"/",
  417. U"0", U"1", U"2", U"3", U"4", U"5", U"6", U"7", U"8", U"9", U":", U";", U"<", U"=", U">", U"?",
  418. U"@", U"A", U"B", U"C", U"D", U"E", U"F", U"G", U"H", U"I", U"J", U"K", U"L", U"M", U"N", U"O",
  419. U"P", U"Q", U"R", U"S", U"T", U"U", U"V", U"W", U"X", U"Y", U"Z", U"[", U"\\", U"]", U"^", U"_",
  420. U"`", U"a", U"b", U"c", U"d", U"e", U"f", U"g", U"h", U"i", U"j", U"k", U"l", U"m", U"n", U"o",
  421. U"p", U"q", U"r", U"s", U"t", U"u", U"v", U"w", U"x", U"y", U"z", U"{", U"|", U"}", U"~", U"Del",
  422. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  423. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  424. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  425. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, U"[", U"]", U",", U"?", U".", U"\\",
  426. U";", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  427. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, U"-", U"`", U"=", U"\'", 0,
  428. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  429. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  430. const conststring32 keyString = keyStrings [acc] ? keyStrings [acc] : U"???";
  431. MelderString_copy (& title, _GuiWin_expandAmpersands (my name.get()), U"\t",
  432. modifiers & _motif_COMMAND_MASK ? U"Ctrl-" : NULL,
  433. modifiers & _motif_OPTION_MASK ? U"Alt-" : NULL,
  434. modifiers & _motif_SHIFT_MASK ? U"Shift-" : NULL, keyString);
  435. }
  436. ModifyMenu (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND | MF_STRING, my nat.entry.id, Melder_peek32toW (title.string));
  437. }
  438. /********** **********/
  439. /*
  440. * We now create the native objects associated with this widget,
  441. * but do not show them on the screen yet (ideally).
  442. * A reference must be made from widget to native object and back.
  443. * On Mac, we normally use the RefCon fields of the windows and controls.
  444. * On Win, we use SetWindowLongPtr (window, GWLP_USERDATA, (LONG_PTR) widget).
  445. */
  446. static void _GuiNativizeWidget (GuiObject me) {
  447. if (my nativized) return;
  448. if (my inMenu) {
  449. if (MEMBER (me, PulldownMenu)) {
  450. int id;
  451. for (id = 1; id <= MAXIMUM_NUMBER_OF_MENUS; id ++) if (! theMenus [id]) break;
  452. my nat.menu.id = id;
  453. theMenus [my nat.menu.id] = me; // instead of UserData fields
  454. /*
  455. * This will be a hierarchical menu.
  456. */
  457. my nat.menu.handle = CreatePopupMenu ();
  458. } else {
  459. /*
  460. * Any menu item (push button, toggle button, or cascade button) shall contain its native parent menu handle.
  461. */
  462. my nat.entry.handle = my parent -> nat.menu.handle;
  463. {
  464. /*
  465. * A Windows menu item shall have a shell-unique ID,
  466. * which we can use to make changes and
  467. * which will be sent to us by the WM_COMMAND message.
  468. * This ID should be higher than 4000, in order to be different from the menu IDs.
  469. * In our implementation, item IDs are application-unique.
  470. */
  471. int id;
  472. for (id = MINIMUM_MENU_ITEM_ID; id <= MAXIMUM_MENU_ITEM_ID; id ++) if (! theMenuItems [id]) break;
  473. my nat.entry.id = id; // install unique ID
  474. theMenuItems [id] = true;
  475. }
  476. }
  477. } else switch (my widgetClass) {
  478. case xmBulletinBoardWidgetClass: {
  479. my window = CreateWindowEx (0, Melder_peek32toW (theWindowClassName), L"bulletinBoard", WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS,
  480. my x, my y, my width, my height, my parent -> window, NULL, theGui.instance, NULL);
  481. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  482. } break;
  483. case xmDrawingAreaWidgetClass: Melder_fatal (U"Should be implemented in GuiDrawingArea."); break;
  484. case xmFormWidgetClass: {
  485. my window = CreateWindowEx (0, Melder_peek32toW (theWindowClassName), L"form", WS_CHILD | WS_CLIPSIBLINGS,
  486. my x, my y, my width, my height, my parent -> window, NULL, theGui.instance, NULL);
  487. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  488. } break;
  489. case xmRowColumnWidgetClass: {
  490. my window = CreateWindowEx (0, Melder_peek32toW (theWindowClassName), L"rowColumn", WS_CHILD | WS_CLIPSIBLINGS,
  491. my x, my y, my width, my height, my parent -> window, NULL, theGui.instance, NULL);
  492. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  493. } break;
  494. case xmListWidgetClass: Melder_fatal (U"Should be implemented in GuiList."); break;
  495. case xmMenuBarWidgetClass: {
  496. if (! my shell -> motiff.shell.isDialog && my shell -> nat.shell.menuBar == NULL && my parent -> widgetClass != xmRowColumnWidgetClass) {
  497. HMENU bar = CreateMenu ();
  498. SetMenu (my shell -> window, bar);
  499. my nat.menu.handle = bar;
  500. my shell -> nat.shell.menuBar = me; // does this have to be?
  501. } else {
  502. my widgetClass = xmRowColumnWidgetClass; // !!!!!!!!!!!!!
  503. my orientation = XmHORIZONTAL;
  504. my rowColumnType = XmMENU_BAR;
  505. my window = CreateWindowEx (0, Melder_peek32toW (theWindowClassName), L"rowColumn", WS_CHILD,
  506. my x, my y, my width, my height, my parent -> window, NULL, theGui.instance, NULL);
  507. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  508. }
  509. } break;
  510. case xmPulldownMenuWidgetClass: {
  511. int id;
  512. for (id = 1; id <= MAXIMUM_NUMBER_OF_MENUS; id ++) if (! theMenus [id]) break;
  513. my nat.menu.id = id;
  514. theMenus [my nat.menu.id] = me; // instead of UserData fields
  515. if (MEMBER (my parent, MenuBar)) {
  516. GuiObject menu;
  517. UINT beforeID = -1;
  518. my nat.menu.handle = CreatePopupMenu ();
  519. /*
  520. * Insert the menu before the Help menu, if that exists; otherwise, at the end.
  521. */
  522. for (menu = my parent -> firstChild; menu; menu = menu -> nextSibling) {
  523. if (MEMBER (menu, PulldownMenu) && str32equ (menu -> name.get(), U"Help") && menu != me) {
  524. beforeID = (UINT) menu -> nat.menu./*handle*/id;
  525. break;
  526. }
  527. }
  528. {
  529. MENUITEMINFO info;
  530. info. cbSize = sizeof (MENUITEMINFO);
  531. info. fMask = MIIM_TYPE | MIIM_SUBMENU | MIIM_ID;
  532. info. fType = MFT_STRING | ( str32equ (my name.get(), U"Help") ? MFT_RIGHTJUSTIFY : 0 );
  533. info. dwTypeData = (mutablestringW) Melder_peek32toW (my name.get());
  534. info. hSubMenu = my nat.menu.handle;
  535. info. wID = (UINT) my nat.menu./*handle*/id;
  536. InsertMenuItem (my parent -> nat.menu.handle, beforeID, 0, & info);
  537. } /*else if (beforeID >= 0)
  538. InsertMenu (my parent -> nat.menu.handle, beforeID, MF_STRING | MF_POPUP | MF_BYCOMMAND,
  539. (UINT) my nat.menu.handle, my name);
  540. else
  541. AppendMenu (my parent -> nat.menu.handle, MF_STRING | MF_POPUP, (UINT) my nat.menu.handle, my name);*/
  542. DrawMenuBar (my shell -> window);
  543. } else if (MEMBER (my parent, RowColumn) && my parent -> rowColumnType == XmMENU_BAR) {
  544. my nat.menu.handle = CreatePopupMenu ();
  545. }
  546. } break;
  547. case xmLabelWidgetClass: Melder_fatal (U"Should be implemented in GuiLabel."); break;
  548. case xmCascadeButtonWidgetClass: {
  549. if (! my motiff.cascadeButton.inBar) {
  550. my window = CreateWindow (L"button", Melder_peek32toW (_GuiWin_expandAmpersands (my name.get())),
  551. WS_CHILD | BS_PUSHBUTTON | WS_CLIPSIBLINGS,
  552. my x, my y, my width, my height, my parent -> window, (HMENU) 1, theGui.instance, NULL);
  553. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  554. SetWindowFont (my window, GetStockFont (ANSI_VAR_FONT), false);
  555. }
  556. } break;
  557. case xmPushButtonWidgetClass: Melder_fatal (U"Should be implemented in GuiButton."); break;
  558. case xmTextWidgetClass: Melder_fatal (U"Should be implemented in GuiText."); break;
  559. case xmToggleButtonWidgetClass: Melder_fatal (U"Should be implemented in GuiCheckButton and GuiRadioButton."); break;
  560. case xmScaleWidgetClass: {
  561. my window = CreateWindow (PROGRESS_CLASS, Melder_peek32toW (_GuiWin_expandAmpersands (my name.get())), WS_CHILD | WS_CLIPSIBLINGS,
  562. my x, my y, my width, my height, my parent -> window, (HMENU) 1, theGui.instance, NULL);
  563. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  564. SendMessage (my window, PBM_SETRANGE, (WPARAM) 0, (LPARAM) MAKELONG (0, 10000));
  565. } break;
  566. case xmScrollBarWidgetClass: {
  567. my window = CreateWindow (L"scrollbar", Melder_peek32toW (my name.get()), WS_CHILD |
  568. ( str32equ (my name.get(), U"verticalScrollBar") ? SBS_VERT : SBS_HORZ ) | WS_CLIPSIBLINGS,
  569. my x, my y, my width, my height, my parent -> window, (HMENU) 1, theGui.instance, NULL);
  570. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  571. NativeScrollBar_set (me);
  572. my minimum = 0;
  573. my maximum = 100;
  574. my value = 0;
  575. my sliderSize = 100;
  576. } break;
  577. case xmScrolledWindowWidgetClass: {
  578. /*
  579. * The space of the scrolled window is completely filled by three of its children:
  580. * the two scroll bars and the clip window. The first child you create yourself will be the
  581. * work window: this work window can grow larger than the clip window.
  582. * While normally every widget is only clipped to the rects of its ancestors,
  583. * for a scrolled window the clip window will be inserted into this chain.
  584. * Example: if the widget hierarchy is
  585. * shell.form.scrolledWindow.column.row.pushButton,
  586. * the clipping hierarchy will be
  587. * (shell.)form.(scrolledWindow.)clipWindow.column.row.pushButton
  588. */
  589. my window = CreateWindowEx (0, Melder_peek32toW (theWindowClassName), L"scrolledWindow", WS_CHILD | WS_CLIPSIBLINGS,
  590. my x, my y, my width, my height, my parent -> window, NULL, theGui.instance, NULL);
  591. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  592. my motiff.scrolledWindow.horizontalBar = XmCreateScrollBar (me, "horizontalScrollBar", NULL, 0);
  593. my motiff.scrolledWindow.verticalBar = XmCreateScrollBar (me, "verticalScrollBar", NULL, 0);
  594. XtVaSetValues (my motiff.scrolledWindow.horizontalBar, XmNorientation, XmHORIZONTAL,
  595. XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM,
  596. XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 15, XmNheight, 16,
  597. XmNminimum, 0, XmNmaximum, 100, XmNsliderSize, 100,
  598. XmNincrement, CELL_HEIGHT, XmNpageIncrement, 101 - CELL_HEIGHT, NULL);
  599. XtVaSetValues (my motiff.scrolledWindow.verticalBar, XmNorientation, XmVERTICAL,
  600. XmNtopAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM,
  601. XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 15, XmNwidth, 16,
  602. XmNminimum, 0, XmNmaximum, 100, XmNsliderSize, 100,
  603. XmNincrement, CELL_HEIGHT, XmNpageIncrement, 101 - CELL_HEIGHT, NULL);
  604. my motiff.scrolledWindow.clipWindow = XmCreateBulletinBoard (me, "clipWindow", NULL, 0);
  605. XtVaSetValues (my motiff.scrolledWindow.clipWindow,
  606. XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 1, // for border
  607. XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 16, // for scroll bar
  608. XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 1, // for border
  609. XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 16, NULL); // for scroll bar
  610. XtAddCallback (my motiff.scrolledWindow.verticalBar, XmNvalueChangedCallback, cb_scroll, (XtPointer) me);
  611. XtAddCallback (my motiff.scrolledWindow.verticalBar, XmNdragCallback, cb_scroll, (XtPointer) me);
  612. XtAddCallback (my motiff.scrolledWindow.horizontalBar, XmNvalueChangedCallback, cb_scroll, (XtPointer) me);
  613. XtAddCallback (my motiff.scrolledWindow.horizontalBar, XmNdragCallback, cb_scroll, (XtPointer) me);
  614. } break;
  615. case xmShellWidgetClass: {
  616. static char32 *className { theApplicationClassName }; // only for first window
  617. my window = CreateWindowEx (theDialogHint ? WS_EX_DLGMODALFRAME /* | WS_EX_TOPMOST */ : 0,
  618. Melder_peek32toW (className), Melder_peek32toW (className),
  619. theDialogHint ? WS_CAPTION | WS_SYSMENU : WS_OVERLAPPEDWINDOW,
  620. CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, my parent ? my parent -> window : NULL, NULL, theGui.instance, NULL);
  621. className = theWindowClassName; // all later windows
  622. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  623. my motiff.shell.isDialog = theDialogHint; // so we can maintain a single Shell class instead of two different
  624. } break;
  625. default: break;
  626. }
  627. my nativized = True;
  628. }
  629. static GuiObject createWidget (int widgetClass, GuiObject parent, const char *name) {
  630. GuiObject me = _Gui_initializeWidget (widgetClass, parent, Melder_peek8to32 (name));
  631. _GuiNativizeWidget (me);
  632. return me;
  633. }
  634. void _Gui_invalidateWidget (GuiObject me) {
  635. if (! my managed) return; // should be: visible
  636. if (MEMBER (me, Shell) /*||
  637. my widgetClass == xmRowColumnWidgetClass ||
  638. my widgetClass == xmFormWidgetClass*/) return; // composites are not invalidated !!!!! ????
  639. }
  640. void _Gui_validateWidget (GuiObject me) {
  641. if (! my managed) return; // should be: visible
  642. if (MEMBER (me, Shell)) return;
  643. }
  644. static void Native_move (GuiObject me, int dx, int dy) {
  645. /* Native_move () changes the native attributes and visual position of the widget:
  646. * No Motif attributes (such as 'x' and 'y') are changed.
  647. * Usage:
  648. * Native_move () is normally called immediately after changing the 'x' or 'y' attribute.
  649. * It can be seen as the 'realization' of a Motif move.
  650. */
  651. (void) dx;
  652. (void) dy;
  653. if (! my window) return; // ignore menu items
  654. if (MEMBER (me, Shell)) {
  655. my nat.shell.duringMoveWindow = True;
  656. if (my motiff.shell.isDialog)
  657. MoveWindow (my window, my x, my y, my width + 2 * GetSystemMetrics (SM_CXFIXEDFRAME),
  658. my height + 2 * GetSystemMetrics (SM_CYFIXEDFRAME) + GetSystemMetrics (SM_CYCAPTION), true);
  659. else
  660. MoveWindow (my window, my x, my y,
  661. my width + 2 * GetSystemMetrics (SM_CXSIZEFRAME),
  662. my height + 2 * GetSystemMetrics (SM_CYSIZEFRAME) + GetSystemMetrics (SM_CYCAPTION) +
  663. ( my nat.shell.menuBar ? GetSystemMetrics (SM_CYMENU) : 0 ), true);
  664. } else
  665. MoveWindow (my window, my x, my y, my width, my height, True);
  666. }
  667. static void shellResizeWidget (GuiObject me, int dx, int dy, int dw, int dh) {
  668. GuiObject child;
  669. Melder_assert (! my shell || ! my shell -> nat.shell.duringMoveWindow);
  670. if (my window && ! MEMBER (me, Shell)) {
  671. /*RECT rect, client;
  672. GetWindowRect (my window, & rect);
  673. GetClientRect (my window, & client);
  674. ScreenToClient (my parent -> window, (LPPOINT) & rect.left);
  675. ScreenToClient (my parent -> window, (LPPOINT) & rect.right);
  676. if (rect.left!=my x||rect.right!=my x + my width||rect.top !=my y||rect.bottom!=my y +my height)
  677. Melder_warning ("class %d x %d left %d y %d top %d width %d right %d height %d bottom %d",
  678. my widgetClass, my x, rect.left, my y, rect.top, my width, rect.right, my height, rect.bottom);*/
  679. MoveWindow (my window, my x, my y, my width, my height, true);
  680. if (MEMBER (me, DrawingArea)) _GuiWinDrawingArea_shellResize (me);
  681. }
  682. for (child = my firstChild; child; child = child -> nextSibling) {
  683. int cdx = 0, cdy = 0, cdw = 0, cdh = 0;
  684. if (MEMBER (child, Shell)) continue;
  685. if (child -> rightAttachment == XmATTACH_FORM) {
  686. if (child -> leftAttachment == XmATTACH_FORM) cdw = dw; else cdx = dw;
  687. }
  688. if (child -> leftAttachment == XmATTACH_POSITION && child -> rightAttachment == XmATTACH_POSITION) {
  689. int xLeft = my width * (child -> leftPosition / 100.0);
  690. int xRight = my width * (child -> rightPosition / 100.0);
  691. cdx = xLeft - child -> x;
  692. cdw = (xRight - xLeft) - child -> width;
  693. }
  694. if (child -> bottomAttachment == XmATTACH_FORM) {
  695. if (child -> topAttachment == XmATTACH_FORM) cdh = dh; else cdy = dh;
  696. }
  697. if (child -> topAttachment == XmATTACH_POSITION && child -> bottomAttachment == XmATTACH_POSITION) {
  698. int yTop = my height * (child -> topPosition / 100.0);
  699. int yBottom = my height * (child -> bottomPosition / 100.0);
  700. cdy = yTop - child -> y;
  701. cdh = (yBottom - yTop) - child -> height;
  702. }
  703. child -> x += cdx;
  704. child -> y += cdy;
  705. child -> width += cdw;
  706. child -> height += cdh;
  707. shellResizeWidget (child, dx + cdx, dy + cdy, cdw, cdh);
  708. }
  709. if (MEMBER (me, ScrolledWindow))
  710. _Gui_manageScrolledWindow (me);
  711. }
  712. static void resizeWidget (GuiObject me, int dw, int dh) {
  713. GuiObject child;
  714. if (my window && ! MEMBER (me, Shell)) {
  715. MoveWindow (my window, my x, my y, my width, my height, true);
  716. if (MEMBER (me, DrawingArea)) _GuiWinDrawingArea_shellResize (me);
  717. }
  718. if (MEMBER2 (me, Form, ScrolledWindow))
  719. for (child = my firstChild; child; child = child -> nextSibling) {
  720. int cdx = 0, cdy = 0, cdw = 0, cdh = 0;
  721. if (child -> widgetClass == xmShellWidgetClass) continue;
  722. if (child -> rightAttachment == XmATTACH_FORM) {
  723. if (child -> leftAttachment == XmATTACH_FORM) cdw = dw; else cdx = dw;
  724. }
  725. if (child -> leftAttachment == XmATTACH_POSITION && child -> rightAttachment == XmATTACH_POSITION) {
  726. int xLeft = my width * (child -> leftPosition / 100.0);
  727. int xRight = my width * (child -> rightPosition / 100.0);
  728. cdx = xLeft - child -> x;
  729. cdw = (xRight - xLeft) - child -> width;
  730. }
  731. if (child -> bottomAttachment == XmATTACH_FORM) {
  732. if (child -> topAttachment == XmATTACH_FORM) cdh = dh; else cdy = dh;
  733. }
  734. if (child -> topAttachment == XmATTACH_POSITION && child -> bottomAttachment == XmATTACH_POSITION) {
  735. int yTop = my height * (child -> topPosition / 100.0);
  736. int yBottom = my height * (child -> bottomPosition / 100.0);
  737. cdy = yTop - child -> y;
  738. cdh = (yBottom - yTop) - child -> height;
  739. }
  740. if (cdx || cdy) {
  741. child -> x += cdx;
  742. child -> y += cdy;
  743. Native_move (child, cdx, cdy);
  744. }
  745. if (cdw || cdh) {
  746. child -> width += cdw;
  747. child -> height += cdh;
  748. resizeWidget (child, cdw, cdh);
  749. }
  750. }
  751. if (MEMBER (me, Shell)) {
  752. int right = 1000, bottom = 500;
  753. for (child = my firstChild; child; child = child -> nextSibling)
  754. if (MEMBER2 (child, Form, ScrolledWindow)) {
  755. child -> width += dw;
  756. child -> height += dh;
  757. resizeWidget (child, dw, dh);
  758. }
  759. }
  760. }
  761. static void _motif_setValues (GuiObject me, va_list arg) {
  762. int resource;
  763. Boolean move = False, resize = False, attach = False, scrollset = False;
  764. char *text;
  765. int oldX = my x, oldY = my y, oldWidth = my width, oldHeight = my height;
  766. while (resource = va_arg (arg, int), resource != 0) switch (resource) {
  767. case XmNautoUnmanage:
  768. my autoUnmanage = va_arg (arg, int);
  769. break;
  770. case XmNbottomAttachment:
  771. my bottomAttachment = va_arg (arg, int);
  772. attach = True;
  773. break;
  774. case XmNbottomOffset:
  775. my bottomOffset = va_arg (arg, int);
  776. attach = True;
  777. break;
  778. case XmNbottomPosition: my bottomPosition = va_arg (arg, int);
  779. attach = True;
  780. break;
  781. case XmNcolumns: {
  782. int columns = va_arg (arg, int);
  783. Melder_assert (MEMBER (me, Text));
  784. my width = columns * 9 + 4;
  785. resize = True;
  786. } break;
  787. case XmNdeleteResponse:
  788. Melder_assert (MEMBER (me, Shell));
  789. my deleteResponse = va_arg (arg, int);
  790. if (my deleteResponse == XmDO_NOTHING && ! my motiff.shell.goAwayCallback) {
  791. // change window attributes
  792. }
  793. break;
  794. case XmNdialogStyle:
  795. Melder_assert (MEMBER2 (me, Form, BulletinBoard));
  796. my shell -> dialogStyle = my dialogStyle = va_arg (arg, int);
  797. break;
  798. case XmNdialogTitle:
  799. Melder_assert (MEMBER2 (me, Form, BulletinBoard));
  800. text = va_arg (arg, char *);
  801. SetWindowTextW (my shell -> window, Melder_peek32toW (Melder_peek8to32 (text)));
  802. break;
  803. case XmNheight:
  804. my height = va_arg (arg, int);
  805. if (MEMBER (me, Shell)) {
  806. int maximumHeight =
  807. GetSystemMetrics (SM_CYMAXIMIZED) - GetSystemMetrics (SM_CYCAPTION) -
  808. GetSystemMetrics (SM_CYSIZEFRAME) - GetSystemMetrics (SM_CYMENU) - 15;
  809. if (my height > maximumHeight) my height = maximumHeight;
  810. }
  811. resize = True;
  812. break;
  813. case XmNhorizontalScrollBar: {
  814. /* Have to kill my own bar first. */
  815. XtDestroyWidget (my motiff.scrolledWindow.horizontalBar);
  816. /* Then replace by new bar. */
  817. my motiff.scrolledWindow.horizontalBar = va_arg (arg, GuiObject);
  818. /* Make sure it is in the right position. */
  819. XtVaSetValues (my motiff.scrolledWindow.horizontalBar, XmNorientation, XmHORIZONTAL,
  820. XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM,
  821. XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 15, XmNheight, 16, NULL);
  822. /*
  823. * Note that we may now have a horizontal bar with application-defined scrolling policy,
  824. * and a vertical bar with automatic scrolling policy.
  825. * This is more flexible than X Motif, but may be simulated in X Motif with the following strategy,
  826. * which is compatible with MotifEmulator:
  827. * - set the scrolling policy to XmAUTOMATIC: two scroll bars will be created;
  828. * - ask for the horizontal scroll bar with XtVaGetValues;
  829. * - remove all the callbacks from the horizontal scroll bar with XtRemoveAllCallbacks;
  830. * - add your application-defined callback with XtAddCallback.
  831. * We just hope now that X Motif does not look into the XmNscrollingPolicy resource after this...
  832. */
  833. } break;
  834. case XmNincrement:
  835. Melder_assert (MEMBER (me, ScrollBar));
  836. my increment = va_arg (arg, int);
  837. break;
  838. case XmNlabelString:
  839. Melder_assert (MEMBER2 (me, CascadeButton, PushButton));
  840. text = va_arg (arg, char *);
  841. my name = Melder_8to32 (text); // BUG throwable
  842. if (my inMenu) {
  843. NativeMenuItem_setText (me);
  844. } else if (MEMBER (me, CascadeButton) && my motiff.cascadeButton.inBar) {
  845. /* BUG: menu title change not implemented */
  846. } else {
  847. _GuiNativeControl_setTitle (me);
  848. }
  849. break;
  850. case XmNleftAttachment: my leftAttachment = va_arg (arg, int);
  851. attach = True;
  852. break;
  853. case XmNleftOffset: my leftOffset = va_arg (arg, int);
  854. attach = True;
  855. break;
  856. case XmNleftPosition: my leftPosition = va_arg (arg, int);
  857. attach = True;
  858. break;
  859. case XmNmaximum:
  860. my maximum = va_arg (arg, int);
  861. if (MEMBER (me, ScrollBar)) scrollset = True;
  862. else if (MEMBER (me, Scale)) _Gui_invalidateWidget (me);
  863. break;
  864. case XmNmenuHelpWidget:
  865. (void) va_arg (arg, GuiObject);
  866. break;
  867. case XmNminimum:
  868. my minimum = va_arg (arg, int);
  869. if (MEMBER (me, ScrollBar)) scrollset = True;
  870. else if (MEMBER (me, Scale)) _Gui_invalidateWidget (me);
  871. break;
  872. case XmNorientation:
  873. Melder_assert (MEMBER3 (me, RowColumn, ScrollBar, Scale));
  874. my orientation = va_arg (arg, int);
  875. break;
  876. case XmNpageIncrement:
  877. Melder_assert (MEMBER (me, ScrollBar));
  878. my pageIncrement = va_arg (arg, int);
  879. break;
  880. case XmNradioBehavior:
  881. Melder_assert (MEMBER (me, RowColumn));
  882. my radioBehavior = va_arg (arg, int);
  883. break;
  884. case XmNrightAttachment:
  885. my rightAttachment = va_arg (arg, int);
  886. attach = True;
  887. break;
  888. case XmNrightOffset:
  889. my rightOffset = va_arg (arg, int);
  890. attach = True;
  891. break;
  892. case XmNrightPosition: my rightPosition = va_arg (arg, int);
  893. attach = True;
  894. break;
  895. case XmNrowColumnType:
  896. Melder_assert (MEMBER (me, RowColumn));
  897. my rowColumnType = va_arg (arg, int);
  898. break;
  899. case XmNrows: (void) va_arg (arg, int); break;
  900. case XmNscaleHeight:
  901. Melder_assert (MEMBER (me, Scale));
  902. my height = va_arg (arg, int);
  903. resize = True;
  904. break;
  905. case XmNscaleWidth:
  906. Melder_assert (MEMBER (me, Scale));
  907. my width = va_arg (arg, int);
  908. resize = True;
  909. break;
  910. case XmNscrollingPolicy: (void) va_arg (arg, int); break;
  911. case XmNsliderSize:
  912. Melder_assert (MEMBER (me, ScrollBar));
  913. my sliderSize = va_arg (arg, int);
  914. scrollset = True;
  915. break;
  916. case XmNspacing:
  917. (void) va_arg (arg, int);
  918. break;
  919. case XmNsubMenuId:
  920. Melder_assert (MEMBER (me, CascadeButton));
  921. my subMenuId = va_arg (arg, GuiObject);
  922. my subMenuId -> popUpButton = me;
  923. break;
  924. case XmNtitle:
  925. Melder_assert (MEMBER (me, Shell));
  926. text = va_arg (arg, char *);
  927. SetWindowTextW (my window, Melder_peek32toW (Melder_peek8to32 (text)));
  928. break;
  929. case XmNtitleString:
  930. Melder_assert (MEMBER (me, Scale));
  931. text = va_arg (arg, char *);
  932. my name = Melder_8to32 (text); // BUG throwable
  933. _Gui_invalidateWidget (me);
  934. break;
  935. case XmNtopAttachment:
  936. my topAttachment = va_arg (arg, int);
  937. attach = True;
  938. break;
  939. case XmNtopOffset:
  940. my topOffset = va_arg (arg, int);
  941. attach = True;
  942. break;
  943. case XmNtopPosition: my topPosition = va_arg (arg, int);
  944. attach = True;
  945. break;
  946. case XmNuserData:
  947. my userData = va_arg (arg, void *);
  948. break;
  949. case XmNvalue:
  950. my value = va_arg (arg, int);
  951. if (MEMBER (me, ScrollBar)) scrollset = True;
  952. else if (MEMBER (me, Scale)) {
  953. SendMessage (my window, PBM_SETPOS, (WPARAM) my value, 0);
  954. }
  955. break;
  956. case XmNverticalScrollBar: {
  957. /* Have to kill my own bar first. */
  958. XtDestroyWidget (my motiff.scrolledWindow.verticalBar);
  959. /* Then replace by new bar. */
  960. my motiff.scrolledWindow.verticalBar = va_arg (arg, GuiObject);
  961. /* Make sure it is in the right position. */
  962. XtVaSetValues (my motiff.scrolledWindow.verticalBar, XmNorientation, XmVERTICAL,
  963. XmNtopAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM,
  964. XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 15, XmNwidth, 16, NULL);
  965. } break;
  966. case XmNwidth:
  967. my width = va_arg (arg, int);
  968. resize = True;
  969. break;
  970. case XmNx: my x = va_arg (arg, int); move = True; break;
  971. case XmNy: my y = va_arg (arg, int); move = True; break;
  972. default: {
  973. if (resource < 0 || resource >= sizeof motif_resourceNames / sizeof (char *))
  974. Melder_flushError (U"(XtVaSetValues:) Resource out of range (", resource, U").");
  975. else
  976. Melder_flushError (U"(XtVaSetValues:) Unknown resource \"", Melder_peek8to32 (motif_resourceNames [resource]), U"\".");
  977. return; // because we do not know how to skip this unknown resource
  978. }
  979. }
  980. if (attach) {
  981. Melder_assert (MEMBER2 (my parent, Form, ScrolledWindow));
  982. if (my leftAttachment == XmATTACH_FORM) {
  983. my x = my leftOffset;
  984. move = True;
  985. if (my rightAttachment == XmATTACH_FORM) {
  986. my width = my parent -> width - my leftOffset - my rightOffset;
  987. resize = True;
  988. }
  989. } else if (my rightAttachment == XmATTACH_FORM) {
  990. my x = my parent -> width - my width - my rightOffset;
  991. move = True;
  992. }
  993. if (my leftAttachment == XmATTACH_POSITION && my rightAttachment == XmATTACH_POSITION) {
  994. my x = my parent -> width * (my leftPosition / 100.0);
  995. int xRight = my parent -> width * (my rightPosition / 100.0);
  996. my width = xRight - my x;
  997. resize = True;
  998. }
  999. if (my topAttachment == XmATTACH_FORM) {
  1000. my y = my topOffset;
  1001. move = True;
  1002. if (my bottomAttachment == XmATTACH_FORM) {
  1003. my height = my parent -> height - my topOffset - my bottomOffset;
  1004. resize = True;
  1005. }
  1006. } else if (my bottomAttachment == XmATTACH_FORM) {
  1007. my y = my parent -> height - my height - my bottomOffset;
  1008. move = True;
  1009. }
  1010. if (my topAttachment == XmATTACH_POSITION && my bottomAttachment == XmATTACH_POSITION) {
  1011. my y = my parent -> height * (my topPosition / 100.0);
  1012. int yBottom = my parent -> height * (my bottomPosition / 100.0);
  1013. my height = yBottom - my y;
  1014. resize = True;
  1015. }
  1016. }
  1017. if (move) {
  1018. Native_move (me, 0, 0);
  1019. }
  1020. if (resize) {
  1021. resizeWidget (me, my width - oldWidth, my height - oldHeight);
  1022. }
  1023. if (scrollset) NativeScrollBar_set (me);
  1024. }
  1025. void _Gui_manageScrolledWindow (GuiObject me) {
  1026. int workWidth, workHeight, horzAutomatic, vertAutomatic;
  1027. GuiObject clipWindow, workWindow, horzBar = my motiff.scrolledWindow.horizontalBar, vertBar = my motiff.scrolledWindow.verticalBar;
  1028. Melder_assert (my widgetClass == xmScrolledWindowWidgetClass);
  1029. clipWindow = my motiff.scrolledWindow.clipWindow;
  1030. workWindow = my motiff.scrolledWindow.workWindow;
  1031. if (clipWindow == NULL || horzBar == NULL || vertBar == NULL) return; // apparently during destruction of scrolled window
  1032. /*
  1033. * We must find out if the scrolling policy of each bar is automatic.
  1034. * Otherwise, we must not change them automatically.
  1035. */
  1036. horzAutomatic = horzBar -> motiff.scrollBar.valueChangedCallbacks.pairs [0].proc == cb_scroll;
  1037. vertAutomatic = vertBar -> motiff.scrollBar.valueChangedCallbacks.pairs [0].proc == cb_scroll;
  1038. /*
  1039. * If the work window has been unmanaged or destroyed, the automatic scroll bars should be empty and insensitive.
  1040. */
  1041. if (workWindow == NULL || ! workWindow -> managed) {
  1042. if (horzAutomatic)
  1043. XtVaSetValues (horzBar, XmNmaximum, 100, XmNsliderSize, 100, XmNvalue, 0, XmNpageIncrement, 1, NULL);
  1044. if (vertAutomatic)
  1045. XtVaSetValues (vertBar, XmNmaximum, 100, XmNsliderSize, 100, XmNvalue, 0, XmNpageIncrement, 1, NULL);
  1046. return;
  1047. }
  1048. workWidth = workWindow -> width > 10 ? workWindow -> width : 10;
  1049. workHeight = workWindow -> height > 10 ? workWindow -> height : 10;
  1050. /*
  1051. * If the scroll bar is automatic, the slider width is set to the visible height of the work window,
  1052. * and the maximum is set to the entire height of the work window.
  1053. * If the value becomes greater than the maximum minus the slider size,
  1054. * the value is reduced and the work window is scrolled up (i.e. moved down).
  1055. */
  1056. if (horzAutomatic) {
  1057. int maximum = workWidth;
  1058. int sliderSize = workWidth < clipWindow -> width ? workWidth : clipWindow -> width;
  1059. int value = horzBar -> value;
  1060. if (value > maximum - sliderSize) {
  1061. value = maximum - sliderSize;
  1062. workWindow -> x += horzBar -> value - value;
  1063. Native_move (workWindow, horzBar -> value - value, 0);
  1064. }
  1065. XtVaSetValues (horzBar, XmNmaximum, maximum, XmNsliderSize, sliderSize, XmNvalue, value,
  1066. XmNpageIncrement, clipWindow -> width - (CELL_HEIGHT - 1), NULL);
  1067. }
  1068. if (vertAutomatic) { /* Automatic? */
  1069. int maximum = workHeight;
  1070. int sliderSize = workHeight < clipWindow -> height ? workHeight : clipWindow -> height;
  1071. int value = vertBar -> value;
  1072. if (value > maximum - sliderSize) {
  1073. value = maximum - sliderSize;
  1074. workWindow -> y += vertBar -> value - value;
  1075. Native_move (workWindow, 0, vertBar -> value - value);
  1076. }
  1077. XtVaSetValues (vertBar, XmNmaximum, maximum, XmNsliderSize, sliderSize, XmNvalue, value,
  1078. XmNpageIncrement, clipWindow -> height - (CELL_HEIGHT - 1), NULL);
  1079. }
  1080. }
  1081. static void _motif_manage (GuiObject me) {
  1082. GuiObject child;
  1083. int x = 2, y = 2;
  1084. int width = 0, height = 0, dw = 0, dh = 0;
  1085. /*if (my widgetClass == xmScrolledWindowWidgetClass) return; /* Ignore. */
  1086. /*
  1087. * My geometry follows the layout of my children.
  1088. * If I am a RowColumn, I am growing while my children move.
  1089. * Because these moves may involve drawing, which needs clipping to my rect,
  1090. * I have to grow with every managed child separately,
  1091. * not just after managing all of them.
  1092. */
  1093. for (child = my firstChild; child; child = child -> nextSibling) {
  1094. if (child -> managed && ! MEMBER (child, Shell)) {
  1095. int dx = 0, dy = 0; // by default, the child does not move
  1096. if (MEMBER (me, RowColumn)) {
  1097. {
  1098. if (x > child -> x) dx = x - child -> x;
  1099. if (y > child -> y) dy = y - child -> y;
  1100. child -> x += dx; x = child -> x;
  1101. child -> y += dy; y = child -> y;
  1102. if (my orientation != XmHORIZONTAL) {
  1103. y += child -> height + 1;
  1104. } else {
  1105. x += child -> width + 3;
  1106. }
  1107. }
  1108. }
  1109. if (child -> x + child -> width > width)
  1110. width = child -> x + child -> width;
  1111. if (child -> y + child -> height > height)
  1112. height = child -> y + child -> height;
  1113. if (MEMBER3 (me, Shell, Form, BulletinBoard)) {
  1114. /* These widgets grow with their children. */
  1115. dw = width - my width, dh = height - my height;
  1116. //dw = 0, dh = 0; // ppgb 20121121
  1117. if (dw < 0) dw = 0;
  1118. if (dh < 0) dh = 0;
  1119. } else if (MEMBER (me, RowColumn)) {
  1120. /* A RowColumn shrinks and grows with its children. */
  1121. dw = width - my width + 2;
  1122. dh = height - my height + 2;
  1123. } else { /* ? */
  1124. dw = width - my width;
  1125. dh = height - my height;
  1126. }
  1127. my width += dw;
  1128. my height += dh;
  1129. /*
  1130. * Now that I have grown, there is room for my child to move.
  1131. */
  1132. if (MEMBER (me, RowColumn)) Native_move (child, dx, dy);
  1133. /*
  1134. * Resize my attached other children.
  1135. */
  1136. #if 0
  1137. if (MEMBER3 (me, RowColumn, Form, ScrolledWindow)) {
  1138. GuiObject child2;
  1139. for (child2 = my firstChild; child2; child2 = child2 -> nextSibling) if (child2 != child && child2 -> managed) {
  1140. int cdx = 0, cdy = 0, cdw = 0, cdh = 0;
  1141. if (child2 -> widgetClass == xmShellWidgetClass) continue;
  1142. if (child2 -> rightAttachment == XmATTACH_FORM)
  1143. if (child2 -> leftAttachment == XmATTACH_FORM) cdw = dw; else cdx = dw;
  1144. if (child2 -> bottomAttachment == XmATTACH_FORM)
  1145. if (child2 -> topAttachment == XmATTACH_FORM) cdh = dh; else cdy = dh;
  1146. if (cdx || cdy) {
  1147. child2 -> x += cdx;
  1148. child2 -> y += cdy;
  1149. Native_move (child2, cdx, cdy);
  1150. }
  1151. if (cdw || cdh) {
  1152. child2 -> width += cdw;
  1153. child2 -> height += cdh;
  1154. resizeWidget (child2, cdw, cdh);
  1155. }
  1156. }
  1157. }
  1158. #endif
  1159. }
  1160. }
  1161. if (my window) Native_move (me, 0, 0);
  1162. /* If I have grown, I have to notify my parent. */
  1163. if (! MEMBER (me, Shell)) {
  1164. //if (MEMBER4 (my parent, RowColumn, Form, BulletinBoard, Shell)) _motif_manage (my parent);
  1165. if (MEMBER (my parent, ScrolledWindow)) _Gui_manageScrolledWindow (my parent);
  1166. }
  1167. }
  1168. /***** X TOOLKIT *****/
  1169. static void xt_addCallback (XtCallbackList *callbacks, XtCallbackProc proc, XtPointer closure) {
  1170. int i;
  1171. for (i = 0; i < MAXNUM_CALLBACKS; i ++) {
  1172. if (! callbacks -> pairs [i]. proc) {
  1173. callbacks -> pairs [i]. proc = proc;
  1174. callbacks -> pairs [i]. closure = closure;
  1175. return;
  1176. }
  1177. }
  1178. Melder_assert (i < MAXNUM_CALLBACKS); // will always fail if we arrive here
  1179. }
  1180. void XtAddCallback (GuiObject me, int kind, XtCallbackProc proc, XtPointer closure) {
  1181. switch (kind) {
  1182. case XmNactivateCallback:
  1183. my activateCallback = proc; my activateClosure = closure;
  1184. break;
  1185. case XmNdestroyCallback:
  1186. my destroyCallback = proc; my destroyClosure = closure;
  1187. break;
  1188. case XmNdragCallback:
  1189. Melder_assert (my widgetClass == xmScrollBarWidgetClass);
  1190. xt_addCallback (& my motiff.scrollBar.dragCallbacks, proc, closure);
  1191. break;
  1192. case XmNmoveCallback:
  1193. Melder_assert (my widgetClass == xmDrawingAreaWidgetClass);
  1194. xt_addCallback (& my motiff.drawingArea.moveCallbacks, proc, closure);
  1195. break;
  1196. case XmNvalueChangedCallback:
  1197. if (my widgetClass == xmScrollBarWidgetClass)
  1198. xt_addCallback (& my motiff.scrollBar.valueChangedCallbacks, proc, closure);
  1199. else if (my widgetClass == xmToggleButtonWidgetClass)
  1200. xt_addCallback (& my motiff.toggleButton.valueChangedCallbacks, proc, closure);
  1201. else Melder_assert (False);
  1202. break;
  1203. default:
  1204. if (kind < 0 || kind >= sizeof motif_resourceNames / sizeof (char *))
  1205. Melder_flushError (U"(XtAddCallback:) Callback name out of range (", kind, U").");
  1206. else
  1207. Melder_flushError (U"(XtAddCallback:) Unknown callback \"", Melder_peek8to32 (motif_resourceNames [kind]), U"\".");
  1208. }
  1209. }
  1210. XtWorkProcId GuiAddWorkProc (XtWorkProc workProc, XtPointer closure) {
  1211. int i = 1;
  1212. while (i < 10 && theWorkProcs [i]) i ++;
  1213. Melder_assert (i < 10);
  1214. theWorkProcs [i] = workProc;
  1215. theWorkProcClosures [i] = closure;
  1216. theNumberOfWorkProcs ++;
  1217. return i;
  1218. }
  1219. void XtRemoveWorkProc (XtWorkProcId id) {
  1220. theWorkProcs [id] = NULL;
  1221. theNumberOfWorkProcs --;
  1222. }
  1223. XtIntervalId GuiAddTimeOut (uinteger interval, XtTimerCallbackProc proc, XtPointer closure) {
  1224. integer i = 1;
  1225. while (i < 10 && theTimeOutProcs [i]) i ++;
  1226. Melder_assert (i < 10);
  1227. theTimeOutProcs [i] = proc;
  1228. theTimeOutStarts [i] = clock ();
  1229. theTimeOutIntervals [i] = (interval * (double) CLOCKS_PER_SEC) / 1000;
  1230. theTimeOutClosures [i] = closure;
  1231. theNumberOfTimeOuts ++;
  1232. return i;
  1233. }
  1234. void XtRemoveTimeOut (XtIntervalId id) {
  1235. theTimeOutProcs [id] = NULL;
  1236. theNumberOfTimeOuts --;
  1237. }
  1238. void XtDestroyWidget (GuiObject me) {
  1239. GuiObject subview = my firstChild;
  1240. /*
  1241. * Prevent subsequent messages.
  1242. */
  1243. HWND natWindow;
  1244. if (my window) SetWindowLongPtr (my window, GWLP_USERDATA, 0);
  1245. if (my widgetClass == xmShellWidgetClass) {
  1246. XtUnmanageChild (me);
  1247. natWindow = my window; // save for destroy
  1248. my window = NULL;
  1249. }
  1250. if (MEMBER2 (me, Form, BulletinBoard) && MEMBER (my parent, Shell) &&
  1251. my parent -> window
  1252. )
  1253. {
  1254. XtDestroyWidget (my parent);
  1255. return;
  1256. }
  1257. while (subview) {
  1258. GuiObject nextSibling = subview -> nextSibling; // save...
  1259. //if (subview -> widgetClass != xmShellWidgetClass) /* FIX instead of Xm's being_destroyed mark. */ // removed 20090105
  1260. XtDestroyWidget (subview);
  1261. subview = nextSibling; // ...because we can't dereference dead subview
  1262. }
  1263. if (my destroyCallback) my destroyCallback (me, my destroyClosure, NULL);
  1264. switch (my widgetClass) {
  1265. case xmLabelWidgetClass: {
  1266. _GuiWinLabel_destroy (me);
  1267. } break;
  1268. case xmCascadeButtonWidgetClass: {
  1269. if (! my inMenu && ! MEMBER (my parent, MenuBar)) _GuiNativeControl_destroy (me);
  1270. } break;
  1271. case xmScaleWidgetClass: {
  1272. _GuiWinScale_destroy (me);
  1273. } break;
  1274. case xmShellWidgetClass: {
  1275. DestroyWindow (natWindow);
  1276. } break;
  1277. case xmListWidgetClass: {
  1278. _GuiWinList_destroy (me);
  1279. } break;
  1280. case xmDrawingAreaWidgetClass: {
  1281. _GuiWinDrawingArea_destroy (me);
  1282. } break;
  1283. case xmRowColumnWidgetClass:
  1284. case xmFormWidgetClass:
  1285. case xmBulletinBoardWidgetClass: {
  1286. DestroyWindow (my window);
  1287. } break;
  1288. case xmTextWidgetClass: {
  1289. _GuiWinText_destroy (me);
  1290. } break;
  1291. case xmPushButtonWidgetClass: {
  1292. if (my inMenu) {
  1293. if (my nat.entry.id) theMenuItems [my nat.entry.id] = false;
  1294. } else {
  1295. _GuiWinButton_destroy (me);
  1296. }
  1297. } break;
  1298. case xmToggleButtonWidgetClass: {
  1299. if (my inMenu) {
  1300. if (my nat.entry.id) theMenuItems [my nat.entry.id] = false;
  1301. } else {
  1302. if (my isRadioButton) {
  1303. _GuiWinRadioButton_destroy (me);
  1304. } else {
  1305. _GuiWinCheckButton_destroy (me);
  1306. }
  1307. }
  1308. } break;
  1309. case xmScrollBarWidgetClass: {
  1310. _GuiWinScrollBar_destroy (me);
  1311. } break;
  1312. case xmScrolledWindowWidgetClass: {
  1313. /* The scroll bars will be destroyed automatically because they are my children. */
  1314. _GuiWinScrolledWindow_destroy (me);
  1315. } break;
  1316. case xmSeparatorWidgetClass: {
  1317. if (my inMenu) {
  1318. if (my nat.entry.id) theMenuItems [my nat.entry.id] = false;
  1319. }
  1320. } break;
  1321. case xmPulldownMenuWidgetClass: {
  1322. if (MEMBER (my parent, MenuBar))
  1323. RemoveMenu (my parent -> nat.menu.handle, (UINT) my nat.menu./*handle*/id, MF_BYCOMMAND);
  1324. DestroyMenu (my nat.menu.handle);
  1325. theMenus [my nat.menu.id] = NULL;
  1326. } break;
  1327. }
  1328. my name. reset(); // not automatic
  1329. if (my parent && me == my parent -> firstChild) // remove dangling reference
  1330. my parent -> firstChild = my nextSibling;
  1331. if (my previousSibling) // remove dangling reference
  1332. my previousSibling -> nextSibling = my nextSibling;
  1333. if (my nextSibling) // remove dangling reference: two-way linked list
  1334. my nextSibling -> previousSibling = my previousSibling;
  1335. if (my parent && MEMBER (my parent, ScrolledWindow)) {
  1336. if (me == my parent -> motiff.scrolledWindow.workWindow) {
  1337. my parent -> motiff.scrolledWindow.workWindow = NULL;
  1338. _Gui_manageScrolledWindow (my parent);
  1339. } else if (me == my parent -> motiff.scrolledWindow.horizontalBar) {
  1340. my parent -> motiff.scrolledWindow.horizontalBar = NULL;
  1341. } else if (me == my parent -> motiff.scrolledWindow.verticalBar) {
  1342. my parent -> motiff.scrolledWindow.verticalBar = NULL;
  1343. } else if (me == my parent -> motiff.scrolledWindow.clipWindow) {
  1344. my parent -> motiff.scrolledWindow.clipWindow = NULL;
  1345. }
  1346. }
  1347. Melder_free (me);
  1348. numberOfWidgets --;
  1349. }
  1350. Boolean XtIsManaged (GuiObject me) { return my managed; }
  1351. Boolean XtIsShell (GuiObject me) {
  1352. return my widgetClass == xmShellWidgetClass;
  1353. }
  1354. void XtMapWidget (GuiObject me) {
  1355. switch (my widgetClass) {
  1356. case xmShellWidgetClass:
  1357. ShowWindow (my window, theGui.commandShow);
  1358. //UpdateWindow (my window);
  1359. break;
  1360. default:
  1361. break;
  1362. }
  1363. }
  1364. static void mapWidget (GuiObject me) {
  1365. GuiObject child;
  1366. Melder_assert (my widgetClass != xmPulldownMenuWidgetClass);
  1367. if (my inMenu) {
  1368. trace (U"showing a menu item");
  1369. int position = NativeMenuItem_getPosition (me);
  1370. switch (my widgetClass) {
  1371. case xmPushButtonWidgetClass: {
  1372. InsertMenu (my nat.entry.handle, position, MF_STRING | MF_BYPOSITION | ( my insensitive ? MF_GRAYED : MF_ENABLED ),
  1373. my nat.entry.id, Melder_peek32toW (_GuiWin_expandAmpersands (my name.get())));
  1374. } break;
  1375. case xmToggleButtonWidgetClass: {
  1376. InsertMenu (my nat.entry.handle, position, MF_STRING | MF_UNCHECKED | MF_BYPOSITION | ( my insensitive ? MF_GRAYED : MF_ENABLED ),
  1377. my nat.entry.id, Melder_peek32toW (_GuiWin_expandAmpersands (my name.get())));
  1378. } break;
  1379. case xmCascadeButtonWidgetClass: {
  1380. my nat.entry.id = (ULONG_PTR) my subMenuId -> nat.menu.handle;
  1381. InsertMenu (my nat.entry.handle, position, MF_POPUP | MF_BYPOSITION | ( my insensitive ? MF_GRAYED : MF_ENABLED ),
  1382. my nat.entry.id, Melder_peek32toW (_GuiWin_expandAmpersands (my name.get())));
  1383. } break;
  1384. case xmSeparatorWidgetClass: {
  1385. InsertMenu (my nat.entry.handle, position, MF_SEPARATOR | MF_BYPOSITION,
  1386. my nat.entry.id, Melder_peek32toW (_GuiWin_expandAmpersands (my name.get())));
  1387. } break;
  1388. }
  1389. } else switch (my widgetClass) {
  1390. case xmBulletinBoardWidgetClass:
  1391. case xmDrawingAreaWidgetClass:
  1392. case xmScrolledWindowWidgetClass:
  1393. case xmFormWidgetClass:
  1394. case xmRowColumnWidgetClass:
  1395. ShowWindow (my window, SW_SHOW); break;
  1396. case xmShellWidgetClass: {
  1397. ShowWindow (my window, theGui.commandShow);
  1398. if (my dialogStyle == XmDIALOG_FULL_APPLICATION_MODAL)
  1399. ;
  1400. } break;
  1401. case xmMenuBarWidgetClass: {
  1402. DrawMenuBar (my shell -> window); // every window has its own menu bar
  1403. } break;
  1404. case xmPushButtonWidgetClass: _GuiNativeControl_show (me); break;
  1405. case xmToggleButtonWidgetClass: _GuiNativeControl_show (me); break;
  1406. case xmScrollBarWidgetClass: {
  1407. if (! my window) {
  1408. my window = CreateWindow (L"scrollbar", Melder_peek32toW (my name.get()), WS_CHILD |
  1409. ( my orientation == XmHORIZONTAL ? SBS_HORZ : SBS_VERT) | WS_CLIPSIBLINGS,
  1410. my x, my y, my width, my height, my parent -> window, (HMENU) 1, theGui.instance, NULL);
  1411. SetWindowLongPtr (my window, GWLP_USERDATA, (LONG_PTR) me);
  1412. NativeScrollBar_set (me);
  1413. }
  1414. _GuiNativeControl_show (me);
  1415. } break;
  1416. case xmLabelWidgetClass: _GuiNativeControl_show (me); break;
  1417. case xmCascadeButtonWidgetClass: {
  1418. if (! MEMBER (my parent, MenuBar)) _GuiNativeControl_show (me);
  1419. } break;
  1420. case xmScaleWidgetClass: {
  1421. _GuiNativeControl_show (me);
  1422. } break;
  1423. case xmTextWidgetClass: {
  1424. _GuiWinText_map (me);
  1425. } break;
  1426. case xmListWidgetClass: {
  1427. _GuiWinList_map (me);
  1428. } break;
  1429. default:
  1430. break;
  1431. }
  1432. for (child = my firstChild; child != NULL; child = child -> nextSibling)
  1433. if (child -> managed) mapWidget (child);
  1434. }
  1435. void XtManageChild (GuiObject me) {
  1436. if (my managed) return;
  1437. if (MEMBER (me, ScrolledWindow)) {
  1438. XtManageChild (my motiff.scrolledWindow.horizontalBar);
  1439. XtManageChild (my motiff.scrolledWindow.verticalBar);
  1440. /*XtManageChild (my motiff.scrolledWindow.clipWindow);*/
  1441. }
  1442. my managed = 1;
  1443. /* Geometry management if my parent is a manager. */
  1444. if (! MEMBER (me, Shell)) {
  1445. if (MEMBER4 (my parent, RowColumn, Form, BulletinBoard, Shell)) _motif_manage (my parent);
  1446. if (MEMBER (me, ScrolledWindow)) _Gui_manageScrolledWindow (me);
  1447. if (MEMBER (my parent, ScrolledWindow)) _Gui_manageScrolledWindow (my parent);
  1448. }
  1449. /* Map to the screen (suppose that mapped_when_managed is true). */
  1450. /* Condition: the entire up chain has been managed. */
  1451. /* Shells or their immediate manager children can be mapped directly. */
  1452. if (my parent && MEMBER (my parent, Shell) && MEMBER2 (me, Form, BulletinBoard)) {
  1453. my parent -> managed = 1;
  1454. mapWidget (my parent);
  1455. } else if (my inMenu) {
  1456. mapWidget (me);
  1457. } else {
  1458. int visible = True;
  1459. GuiObject widget;
  1460. for (widget = me; widget != NULL; widget = widget -> parent) {
  1461. if (! widget -> managed && // if a parent is invisible, so are its children
  1462. ! MEMBER (widget, PulldownMenu)) // the exception: "shown" even if not popped up
  1463. { visible = False; break; }
  1464. if (MEMBER (widget, Shell)) break; // root: end of chain
  1465. }
  1466. if (visible) mapWidget (me);
  1467. }
  1468. /* I may have been created by XmCreateScrolledText or XmCreateScrolledList. */
  1469. /* In that case, my parent should be managed. */
  1470. if (MEMBER2 (me, Text, List) && MEMBER (my parent, ScrolledWindow)) XtManageChild (my parent);
  1471. }
  1472. void XtManageChildren (GuiObjectList children, Cardinal num_children) {
  1473. Cardinal i;
  1474. for (i = 0; i < num_children; i ++) XtManageChild (children [i]);
  1475. }
  1476. void XtSetSensitive (GuiObject me, Boolean value) {
  1477. if (my insensitive != value) return;
  1478. my insensitive = ! value;
  1479. if (! my parent) return;
  1480. switch (my widgetClass) {
  1481. case xmPushButtonWidgetClass:
  1482. case xmToggleButtonWidgetClass: {
  1483. if (my inMenu) {
  1484. NativeMenuItem_setSensitive (me);
  1485. } else {
  1486. _GuiNativeControl_setSensitive (me);
  1487. }
  1488. } break;
  1489. case xmScrollBarWidgetClass: _GuiNativeControl_setSensitive (me); break;
  1490. case xmLabelWidgetClass: _GuiNativeControl_setSensitive (me); break;
  1491. case xmCascadeButtonWidgetClass: {
  1492. if (my inMenu || my motiff.cascadeButton.inBar) {
  1493. if (my subMenuId) {
  1494. if (value) {
  1495. NativeMenuItem_setSensitive (my subMenuId);
  1496. } else {
  1497. NativeMenuItem_setSensitive (my subMenuId);
  1498. }
  1499. DrawMenuBar (my shell -> window);
  1500. }
  1501. } else {
  1502. _GuiNativeControl_setSensitive (me);
  1503. }
  1504. } break;
  1505. case xmPulldownMenuWidgetClass: {
  1506. if (my popUpButton)
  1507. XtSetSensitive (my popUpButton, value);
  1508. } break;
  1509. default:
  1510. break;
  1511. }
  1512. }
  1513. void XtUnmanageChild (GuiObject me) {
  1514. if (! my managed) return;
  1515. if (my inMenu) {
  1516. if (! MEMBER (me, PulldownMenu)) NativeMenuItem_delete (me);
  1517. } else switch (my widgetClass) {
  1518. case xmShellWidgetClass:
  1519. _GuiText_handleFocusLoss (my textFocus);
  1520. ShowWindow (my window, SW_HIDE);
  1521. if (my firstChild && MEMBER2 (my firstChild, Form, BulletinBoard))
  1522. my firstChild -> managed = 0;
  1523. break;
  1524. case xmPushButtonWidgetClass: _GuiNativeControl_hide (me); break;
  1525. case xmToggleButtonWidgetClass: _GuiNativeControl_hide (me); break;
  1526. case xmLabelWidgetClass: _GuiNativeControl_hide (me); break;
  1527. case xmCascadeButtonWidgetClass:
  1528. if (! MEMBER (my parent, MenuBar)) _GuiNativeControl_hide (me);
  1529. break;
  1530. case xmScrollBarWidgetClass: _GuiNativeControl_hide (me); break;
  1531. case xmFormWidgetClass:
  1532. case xmBulletinBoardWidgetClass:
  1533. if (MEMBER (my parent, Shell)) XtUnmanageChild (my parent);
  1534. break;
  1535. case xmTextWidgetClass: {
  1536. _GuiText_unmanage (me);
  1537. } break;
  1538. default:
  1539. _Gui_invalidateWidget (me);
  1540. break;
  1541. }
  1542. my managed = 0;
  1543. if (! MEMBER (me, Shell)) {
  1544. if (MEMBER4 (my parent, RowColumn, Form, BulletinBoard, Shell)) _motif_manage (my parent);
  1545. else if (MEMBER (my parent, ScrolledWindow)) _Gui_manageScrolledWindow (my parent);
  1546. }
  1547. }
  1548. void XtUnmanageChildren (GuiObjectList children, Cardinal num_children) {
  1549. Cardinal i;
  1550. for (i = 0; i < num_children; i ++) XtUnmanageChild (children [i]);
  1551. }
  1552. static LRESULT CALLBACK windowProc (HWND window, UINT message, WPARAM wParam, LPARAM lParam);
  1553. void GuiAppInitialize (const char *name, unsigned int argc, char **argv)
  1554. {
  1555. (void) argc;
  1556. {
  1557. HWND window;
  1558. WNDCLASSEX windowClass;
  1559. Melder_sprint (theApplicationName,100, Melder_peek8to32 (argv [0]));
  1560. Melder_sprint (theApplicationClassName,100, U"PraatShell", PRAAT_WINDOW_CLASS_NUMBER, U" ", theApplicationName);
  1561. Melder_sprint (theWindowClassName,100, U"PraatChildWindow", PRAAT_WINDOW_CLASS_NUMBER, U" ", theApplicationName);
  1562. Melder_sprint (theDrawingAreaClassName,100, U"PraatDrawingArea", PRAAT_WINDOW_CLASS_NUMBER, U" ", theApplicationName);
  1563. window = FindWindow (Melder_peek32toW (theWindowClassName), NULL);
  1564. if (window != NULL) {
  1565. /*
  1566. * We are in the second instance of Praat.
  1567. * The user double-clicked Praat while it was running,
  1568. * or she dropped a file on the Praat icon,
  1569. * or she double-clicked a Praat file.
  1570. */
  1571. if (IsIconic (window)) ShowWindow (window, SW_RESTORE);
  1572. SetForegroundWindow (window);
  1573. if (theOpenDocumentCallback) {
  1574. for (unsigned int iarg = 1; iarg < argc; iarg ++) {
  1575. if (argv [iarg] [0] != '-') {
  1576. structMelderDir dir { };
  1577. Melder_sprint (dir. path,kMelder_MAXPATH+1, Melder_getShellDirectory ());
  1578. Melder_setDefaultDir (& dir);
  1579. structMelderFile file { };
  1580. Melder_relativePathToFile (Melder_peek8to32 (argv [iarg]), & file);
  1581. theOpenDocumentCallback (& file);
  1582. }
  1583. }
  1584. }
  1585. exit (0); // possible problem
  1586. }
  1587. windowClass. cbSize = sizeof (WNDCLASSEX);
  1588. windowClass. style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS |
  1589. CS_OWNDC // crucial: see GraphicsScreen_init ()
  1590. ;
  1591. windowClass. lpfnWndProc = windowProc;
  1592. windowClass. cbClsExtra = 0;
  1593. windowClass. cbWndExtra = 0;
  1594. windowClass. hInstance = theGui.instance;
  1595. windowClass. hIcon = NULL;
  1596. windowClass. hCursor = LoadCursor (NULL, IDC_ARROW);
  1597. windowClass. hbrBackground = /*(HBRUSH) (COLOR_WINDOW + 1)*/ GetStockBrush (LTGRAY_BRUSH);
  1598. windowClass. lpszMenuName = NULL;
  1599. windowClass. lpszClassName = Melder_32toW (theWindowClassName).transfer();
  1600. windowClass. hIconSm = NULL;
  1601. RegisterClassEx (& windowClass);
  1602. windowClass. hbrBackground = GetStockBrush (WHITE_BRUSH);
  1603. windowClass. lpszClassName = Melder_32toW (theDrawingAreaClassName).transfer();
  1604. RegisterClassEx (& windowClass);
  1605. windowClass. lpszClassName = Melder_32toW (theApplicationClassName).transfer();
  1606. RegisterClassEx (& windowClass);
  1607. InitCommonControls ();
  1608. }
  1609. }
  1610. void GuiApp_setApplicationShell (GuiObject shell) {
  1611. theApplicationShell = shell;
  1612. }
  1613. GuiObject XtVaCreateManagedWidget (const char *name, int widgetClass, GuiObject parent, ...) {
  1614. GuiObject me;
  1615. va_list arg;
  1616. va_start (arg, parent);
  1617. me = createWidget (widgetClass, parent, name);
  1618. _motif_setValues (me, arg);
  1619. va_end (arg);
  1620. XtManageChild (me);
  1621. return me;
  1622. }
  1623. GuiObject XtVaCreateWidget (const char *name, int widgetClass, GuiObject parent, ...) {
  1624. GuiObject me;
  1625. va_list arg;
  1626. va_start (arg, parent);
  1627. me = createWidget (widgetClass, parent, name);
  1628. _motif_setValues (me, arg);
  1629. va_end (arg);
  1630. return me;
  1631. }
  1632. void XtVaGetValues (GuiObject me, ...) {
  1633. char *text;
  1634. unsigned int resource;
  1635. va_list arg;
  1636. va_start (arg, me);
  1637. while ((resource = va_arg (arg, int)) != 0) switch (resource) {
  1638. case XmNx: *va_arg (arg, int *) = my x; break;
  1639. case XmNy: *va_arg (arg, int *) = my y; break;
  1640. case XmNwidth: *va_arg (arg, int *) = my width; break;
  1641. case XmNheight: *va_arg (arg, int *) = my height; break;
  1642. case XmNuserData: *va_arg (arg, void **) = my userData; break;
  1643. case XmNtitle:
  1644. Melder_assert (my widgetClass == xmShellWidgetClass);
  1645. *va_arg (arg, char **) = NULL;
  1646. break;
  1647. case XmNlabelString:
  1648. case XmNtitleString:
  1649. Melder_assert (my widgetClass == xmCascadeButtonWidgetClass || my widgetClass == xmScaleWidgetClass);
  1650. text = Melder_32to8 (my name.get()).transfer(); // BUG throwable
  1651. *va_arg (arg, char **) = text;
  1652. break;
  1653. case XmNdialogTitle:
  1654. Melder_assert (my widgetClass == xmFormWidgetClass || my widgetClass == xmBulletinBoardWidgetClass);
  1655. *va_arg (arg, char **) = NULL; // NYI
  1656. break;
  1657. case XmNradioBehavior:
  1658. Melder_assert (my widgetClass == xmRowColumnWidgetClass);
  1659. *va_arg (arg, int *) = my radioBehavior;
  1660. break;
  1661. case XmNautoUnmanage:
  1662. *va_arg (arg, int *) = my autoUnmanage;
  1663. break;
  1664. case XmNorientation:
  1665. Melder_assert (my widgetClass == xmRowColumnWidgetClass ||
  1666. my widgetClass == xmScrollBarWidgetClass);
  1667. *va_arg (arg, int *) = my orientation;
  1668. break;
  1669. case XmNrowColumnType:
  1670. Melder_assert (my widgetClass == xmRowColumnWidgetClass);
  1671. *va_arg (arg, int *) = my rowColumnType;
  1672. break;
  1673. case XmNsubMenuId:
  1674. Melder_assert (my widgetClass == xmCascadeButtonWidgetClass);
  1675. *va_arg (arg, GuiObject *) = my subMenuId;
  1676. break;
  1677. case XmNdialogStyle:
  1678. Melder_assert (my widgetClass == xmFormWidgetClass ||
  1679. my widgetClass == xmBulletinBoardWidgetClass);
  1680. *va_arg (arg, int *) = my dialogStyle;
  1681. break;
  1682. case XmNleftAttachment:
  1683. Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
  1684. *va_arg (arg, int *) = my leftAttachment;
  1685. break;
  1686. case XmNrightAttachment:
  1687. Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
  1688. *va_arg (arg, int *) = my rightAttachment;
  1689. break;
  1690. case XmNtopAttachment:
  1691. Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
  1692. *va_arg (arg, int *) = my topAttachment;
  1693. break;
  1694. case XmNbottomAttachment:
  1695. Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
  1696. *va_arg (arg, int *) = my bottomAttachment;
  1697. break;
  1698. case XmNleftOffset:
  1699. Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
  1700. *va_arg (arg, int *) = my leftOffset;
  1701. break;
  1702. case XmNrightOffset:
  1703. Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
  1704. *va_arg (arg, int *) = my rightOffset;
  1705. break;
  1706. case XmNtopOffset:
  1707. Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
  1708. *va_arg (arg, int *) = my topOffset;
  1709. break;
  1710. case XmNbottomOffset:
  1711. Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
  1712. *va_arg (arg, int *) = my bottomOffset;
  1713. break;
  1714. case XmNminimum: *va_arg (arg, int *) = my minimum; break;
  1715. case XmNmaximum: *va_arg (arg, int *) = my maximum; break;
  1716. case XmNvalue: *va_arg (arg, int *) = my value; break;
  1717. case XmNincrement:
  1718. Melder_assert (my widgetClass == xmScrollBarWidgetClass);
  1719. *va_arg (arg, int *) = my increment;
  1720. break;
  1721. case XmNpageIncrement:
  1722. Melder_assert (my widgetClass == xmScrollBarWidgetClass);
  1723. *va_arg (arg, int *) = my pageIncrement;
  1724. break;
  1725. case XmNsliderSize:
  1726. Melder_assert (my widgetClass == xmScrollBarWidgetClass);
  1727. *va_arg (arg, int *) = my sliderSize;
  1728. break;
  1729. case XmNdeleteResponse:
  1730. Melder_assert (my widgetClass == xmShellWidgetClass);
  1731. *va_arg (arg, int *) = my deleteResponse;
  1732. break;
  1733. case XmNcolumns: *va_arg (arg, int *) = 40; break;
  1734. case XmNhorizontalScrollBar: *va_arg (arg, GuiObject *) = my motiff.scrolledWindow.horizontalBar; break;
  1735. case XmNverticalScrollBar: *va_arg (arg, GuiObject *) = my motiff.scrolledWindow.verticalBar; break;
  1736. default: {
  1737. if (resource < 0 || resource >= sizeof motif_resourceNames / sizeof (char *))
  1738. Melder_flushError (U"(XtVaGetValues:) Resource out of range (", resource, U").");
  1739. else
  1740. Melder_flushError (U"(XtVaGetValues:) Unknown resource \"", Melder_peek8to32 (motif_resourceNames [resource]), U"\".");
  1741. return;
  1742. }
  1743. }
  1744. va_end (arg);
  1745. }
  1746. void XtVaSetValues (GuiObject me, ...) {
  1747. va_list arg;
  1748. va_start (arg, me);
  1749. _motif_setValues (me, arg);
  1750. va_end (arg);
  1751. }
  1752. Window XtWindow (GuiObject me) {
  1753. return (Window) my window;
  1754. }
  1755. /***** MOTIF *****/
  1756. void XmAddWMProtocolCallback (GuiObject me, Atom protocol, XtCallbackProc callback, char *closure) {
  1757. if (protocol == 'delw') {
  1758. my motiff.shell.goAwayCallback = callback;
  1759. my motiff.shell.goAwayClosure = closure;
  1760. }
  1761. }
  1762. GuiObject XmCreateBulletinBoard (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1763. (void) dum1;
  1764. (void) dum2;
  1765. return createWidget (xmBulletinBoardWidgetClass, parent, name);
  1766. }
  1767. GuiObject XmCreateBulletinBoardDialog (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1768. GuiObject shell = XmCreateDialogShell (parent, name, dum1, dum2);
  1769. return XmCreateBulletinBoard (shell, name, dum1, dum2);
  1770. }
  1771. GuiObject XmCreateCascadeButton (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1772. (void) dum1;
  1773. (void) dum2;
  1774. return createWidget (xmCascadeButtonWidgetClass, parent, name);
  1775. }
  1776. GuiObject XmCreateCascadeButtonGadget (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1777. (void) dum1;
  1778. (void) dum2;
  1779. return createWidget (xmCascadeButtonGadgetClass, parent, name);
  1780. }
  1781. GuiObject XmCreateDialogShell (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1782. GuiObject shell;
  1783. theDialogHint = True;
  1784. shell = XmCreateShell (parent, name, dum1, dum2);
  1785. theDialogHint = False;
  1786. return shell;
  1787. }
  1788. GuiObject XmCreateForm (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1789. (void) dum1;
  1790. (void) dum2;
  1791. return createWidget (xmFormWidgetClass, parent, name);
  1792. }
  1793. GuiObject XmCreateFormDialog (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1794. GuiObject shell = XmCreateDialogShell (parent, name, dum1, dum2);
  1795. return XmCreateForm (shell, name, dum1, dum2);
  1796. }
  1797. GuiObject XmCreateMenuBar (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1798. (void) dum1;
  1799. (void) dum2;
  1800. return createWidget (xmMenuBarWidgetClass, parent, name);
  1801. }
  1802. GuiObject XmCreatePulldownMenu (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1803. (void) dum1;
  1804. (void) dum2;
  1805. return createWidget (xmPulldownMenuWidgetClass, parent, name);
  1806. }
  1807. GuiObject XmCreateRadioBox (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1808. GuiObject result = createWidget (xmRowColumnWidgetClass, parent, name);
  1809. (void) dum1;
  1810. (void) dum2;
  1811. XtVaSetValues (result, XmNradioBehavior, True, NULL);
  1812. return result;
  1813. }
  1814. GuiObject XmCreateRowColumn (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1815. (void) dum1;
  1816. (void) dum2;
  1817. return createWidget (xmRowColumnWidgetClass, parent, name);
  1818. }
  1819. GuiObject XmCreateScale (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1820. (void) dum1;
  1821. (void) dum2;
  1822. return createWidget (xmScaleWidgetClass, parent, name);
  1823. }
  1824. GuiObject XmCreateScrollBar (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1825. (void) dum1;
  1826. (void) dum2;
  1827. return createWidget (xmScrollBarWidgetClass, parent, name);
  1828. }
  1829. GuiObject XmCreateScrolledWindow (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1830. (void) dum1;
  1831. (void) dum2;
  1832. return createWidget (xmScrolledWindowWidgetClass, parent, name);
  1833. }
  1834. GuiObject XmCreateSeparator (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1835. (void) dum1;
  1836. (void) dum2;
  1837. return createWidget (xmSeparatorWidgetClass, parent, name);
  1838. }
  1839. GuiObject XmCreateSeparatorGadget (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1840. (void) dum1;
  1841. (void) dum2;
  1842. return createWidget (xmSeparatorWidgetClass, parent, name);
  1843. }
  1844. GuiObject XmCreateShell (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1845. GuiObject me = createWidget (xmShellWidgetClass, parent, name);
  1846. (void) dum1;
  1847. (void) dum2;
  1848. return me;
  1849. }
  1850. GuiObject XmCreateToggleButtonGadget (GuiObject parent, const char *name, ArgList dum1, int dum2) {
  1851. (void) dum1;
  1852. (void) dum2;
  1853. return createWidget (xmToggleButtonWidgetClass, parent, name);
  1854. }
  1855. void XmScaleGetValue (GuiObject me, int *value_return) {
  1856. Melder_assert (my widgetClass == xmScaleWidgetClass);
  1857. *value_return = my value;
  1858. }
  1859. void XmScaleSetValue (GuiObject me, int value) {
  1860. Melder_assert (my widgetClass == xmScaleWidgetClass);
  1861. my value = value;
  1862. SendMessage (my window, PBM_SETPOS, (WPARAM) value, 0);
  1863. _motif_update (me, 0);
  1864. }
  1865. void XmScrollBarGetValues (GuiObject me, int *value, int *sliderSize,
  1866. int *increment, int *pageIncrement)
  1867. {
  1868. Melder_assert (my widgetClass == xmScrollBarWidgetClass);
  1869. *value = my value;
  1870. *sliderSize = my sliderSize;
  1871. *increment = my increment;
  1872. *pageIncrement = my pageIncrement;
  1873. }
  1874. void XmScrollBarSetValues (GuiObject me, int value, int sliderSize,
  1875. int increment, int pageIncrement, Boolean notify)
  1876. {
  1877. Melder_assert (my widgetClass == xmScrollBarWidgetClass);
  1878. my value = value;
  1879. my sliderSize = sliderSize;
  1880. my increment = increment;
  1881. my pageIncrement = pageIncrement;
  1882. NativeScrollBar_set (me);
  1883. if (notify) _Gui_callCallbacks (me, & my motiff.scrollBar.valueChangedCallbacks, NULL);
  1884. }
  1885. Boolean XmToggleButtonGadgetGetState (GuiObject me) {
  1886. Melder_assert (MEMBER (me, ToggleButton));
  1887. Melder_assert (my inMenu);
  1888. return GetMenuState (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND) & MF_CHECKED ? True : False;
  1889. }
  1890. void XmToggleButtonGadgetSetState (GuiObject me, Boolean value, Boolean notify) {
  1891. Melder_assert (MEMBER (me, ToggleButton));
  1892. Melder_assert (my inMenu);
  1893. NativeMenuItem_check (me, value);
  1894. if (notify) _Gui_callCallbacks (me, & my motiff.toggleButton.valueChangedCallbacks, NULL);
  1895. }
  1896. static void _motif_update (GuiObject me, void *event) { (void) me; (void) event; }
  1897. /***** EVENT *****/
  1898. static void _motif_inspectTextWidgets (GuiObject me, GuiObject text,
  1899. integer *p_numberOfTextWidgets, integer *p_textWidgetLocation)
  1900. {
  1901. for (GuiObject sub = my firstChild; sub != NULL; sub = sub -> nextSibling) {
  1902. if (MEMBER (sub, Shell)) continue;
  1903. if (MEMBER (sub, Text)) {
  1904. (*p_numberOfTextWidgets) ++;
  1905. if (sub == text) {
  1906. *p_textWidgetLocation = *p_numberOfTextWidgets;
  1907. }
  1908. } else {
  1909. _motif_inspectTextWidgets (sub, text, p_numberOfTextWidgets, p_textWidgetLocation);
  1910. }
  1911. }
  1912. }
  1913. static GuiObject _motif_getLocatedTextWidget (GuiObject me,
  1914. integer *p_itextWidget, integer textWidgetLocation)
  1915. {
  1916. for (GuiObject sub = my firstChild; sub != NULL; sub = sub -> nextSibling) {
  1917. if (MEMBER (sub, Shell)) continue;
  1918. if (MEMBER (sub, Text)) {
  1919. (*p_itextWidget) ++;
  1920. if (*p_itextWidget == textWidgetLocation) {
  1921. return sub;
  1922. }
  1923. } else {
  1924. GuiObject locatedTextWidget = _motif_getLocatedTextWidget (sub, p_itextWidget, textWidgetLocation);
  1925. if (locatedTextWidget) return locatedTextWidget;
  1926. }
  1927. }
  1928. return NULL;
  1929. }
  1930. static GuiObject _motif_getNextTextWidget (GuiObject shell, GuiObject text, bool backward) {
  1931. integer numberOfTextWidgets = 0, textWidgetLocation = 0;
  1932. _motif_inspectTextWidgets (shell, text, & numberOfTextWidgets, & textWidgetLocation);
  1933. trace (U"Found ", numberOfTextWidgets, U" text widgets.");
  1934. if (numberOfTextWidgets == 0) return NULL; // no tab navigation if there is no text widget (shouldn't normally occur)
  1935. Melder_assert (textWidgetLocation >= 1);
  1936. Melder_assert (textWidgetLocation <= numberOfTextWidgets);
  1937. if (numberOfTextWidgets == 1) return NULL; // no tab navigation if there is only one text widget
  1938. if (backward) {
  1939. textWidgetLocation --; // tab to previous text widget
  1940. if (textWidgetLocation < 1) textWidgetLocation = numberOfTextWidgets; // if at beginning, then tab around to last text widget
  1941. } else {
  1942. textWidgetLocation ++; // tab to next text widget
  1943. if (textWidgetLocation > numberOfTextWidgets) textWidgetLocation = 1; // if at end, then tab around to first text widget
  1944. }
  1945. integer itextWidget = 0;
  1946. return _motif_getLocatedTextWidget (shell, & itextWidget, textWidgetLocation);
  1947. }
  1948. static void on_scroll (GuiObject me, UINT part, int pos) {
  1949. if (my maximum == my minimum) return;
  1950. switch (part) {
  1951. case SB_LINEUP: my value -= my increment; break;
  1952. case SB_LINEDOWN: my value += my increment; break;
  1953. case SB_PAGEUP: my value -= my pageIncrement; break;
  1954. case SB_PAGEDOWN: my value += my pageIncrement; break;
  1955. #if SCROLL32
  1956. case SB_THUMBTRACK: case SB_THUMBPOSITION: my value = my minimum +
  1957. pos * ((my maximum - my minimum - my sliderSize) / (32767.0 - (32767.0 * my sliderSize) / (my maximum - my minimum))); break;
  1958. #else
  1959. case SB_THUMBTRACK: case SB_THUMBPOSITION: my value = pos; break;
  1960. #endif
  1961. default: break;
  1962. }
  1963. if (my value < my minimum) my value = my minimum;
  1964. if (my value > my maximum - my sliderSize) my value = my maximum - my sliderSize;
  1965. NativeScrollBar_set (me);
  1966. if (part == SB_THUMBTRACK || part == SB_THUMBPOSITION)
  1967. _Gui_callCallbacks (me, & my motiff.scrollBar.dragCallbacks, (XtPointer) (ULONG_PTR) part);
  1968. else
  1969. _Gui_callCallbacks (me, & my motiff.scrollBar.valueChangedCallbacks, (XtPointer) (ULONG_PTR) part);
  1970. }
  1971. void XtNextEvent (XEvent *xevent) {
  1972. GetMessage (xevent, NULL, 0, 0);
  1973. }
  1974. static void processWorkProcsAndTimeOuts () {
  1975. if (theNumberOfWorkProcs) {
  1976. for (integer i = 9; i >= 1; i --) {
  1977. if (theWorkProcs [i]) {
  1978. if (theWorkProcs [i] (theWorkProcClosures [i])) XtRemoveWorkProc (i);
  1979. }
  1980. }
  1981. }
  1982. if (theNumberOfTimeOuts) {
  1983. clock_t now = clock ();
  1984. for (integer i = 1; i < 10; i ++) if (theTimeOutProcs [i]) {
  1985. static volatile clock_t timeElapsed; // careful: use 32-bit integers circularly; prevent optimization
  1986. timeElapsed = now - theTimeOutStarts [i];
  1987. if (timeElapsed > theTimeOutIntervals [i]) {
  1988. theTimeOutProcs [i] (theTimeOutClosures [i], & i);
  1989. XtRemoveTimeOut (i);
  1990. }
  1991. }
  1992. }
  1993. }
  1994. void GuiNextEvent (XEvent *xevent) {
  1995. if (theNumberOfWorkProcs != 0 || theNumberOfTimeOuts != 0) {
  1996. if (PeekMessage (xevent, 0, 0, 0, PM_REMOVE)) { // Message available?
  1997. ; // Hand message to XtDispatchEvent.
  1998. } else {
  1999. processWorkProcsAndTimeOuts (); // Handle chores during idle time.
  2000. xevent -> message = 0; // Hand null message to XtDispatchEvent.
  2001. }
  2002. } else GetMessage (xevent, NULL, 0, 0); // be neighbour-friendly: do not hand null events
  2003. }
  2004. static int win_shell_processKeyboardEquivalent (GuiObject me, int kar, int modifiers) {
  2005. for (int imenu = 1; imenu <= MAXIMUM_NUMBER_OF_MENUS; imenu ++) if (theMenus [imenu] && theMenus [imenu] -> shell == me) {
  2006. for (GuiObject child = theMenus [imenu] -> firstChild; child != NULL; child = child -> nextSibling) {
  2007. if ((child -> widgetClass == xmPushButtonWidgetClass || child -> widgetClass == xmToggleButtonWidgetClass) &&
  2008. child -> motiff.pushButton.acceleratorChar == kar &&
  2009. child -> motiff.pushButton.acceleratorModifiers == modifiers) {
  2010. if (child -> activateCallback && ! child -> insensitive) {
  2011. child -> activateCallback (child, child -> activateClosure, 0);
  2012. return 1;
  2013. } else if (child -> widgetClass == xmToggleButtonWidgetClass) {
  2014. XmToggleButtonGadgetSetState (child, 1 - XmToggleButtonGadgetGetState (child), False);
  2015. _Gui_callCallbacks (child, & child -> motiff.toggleButton.valueChangedCallbacks, 0);
  2016. return 1;
  2017. }
  2018. }
  2019. }
  2020. }
  2021. return 0;
  2022. }
  2023. static int win_processKeyboardEquivalent (GuiObject me, int kar, int modifiers) {
  2024. /*
  2025. * First try to send the key command to the active shell.
  2026. * If that fails, try to send the key command to the application shell.
  2027. */
  2028. if (win_shell_processKeyboardEquivalent (me, kar, modifiers)) return 1;
  2029. if (win_shell_processKeyboardEquivalent (theApplicationShell, kar, modifiers)) return 1;
  2030. return 0;
  2031. }
  2032. static GuiObject _motif_findDrawingArea (GuiObject me) {
  2033. if (my widgetClass == xmDrawingAreaWidgetClass) return me;
  2034. for (GuiObject sub = my firstChild; sub != NULL; sub = sub -> nextSibling)
  2035. if (! MEMBER (sub, Shell)) { // only in same top window
  2036. GuiObject drawingArea = _motif_findDrawingArea (sub);
  2037. if (drawingArea) return drawingArea;
  2038. }
  2039. return NULL; // no DrawingArea found
  2040. }
  2041. void XtDispatchEvent (XEvent *xevent) {
  2042. MSG *message = (MSG *) xevent;
  2043. if (message -> message == 0) return; // null message from PeekMessage during work proc or time out.
  2044. /*if (message -> message == WM_KEYDOWN || message -> message == WM_SYSKEYDOWN)
  2045. {
  2046. int kar = LOWORD (message -> wParam);
  2047. int modifiers = 0;
  2048. GuiObject me = (GuiObject) GetWindowLongPtr (message -> hwnd, GWLP_USERDATA);
  2049. if (GetKeyState (VK_CONTROL) < 0) modifiers |= _motif_COMMAND_MASK;
  2050. if (GetKeyState (VK_MENU) < 0) modifiers |= _motif_OPTION_MASK;
  2051. if (GetKeyState (VK_SHIFT) < 0) modifiers |= _motif_SHIFT_MASK;
  2052. if(kar>=48)Melder_flushError ("modifiers:%s%s%s\nmessage: %s\nkar: %d",
  2053. modifiers & _motif_COMMAND_MASK ? " control" : "",
  2054. modifiers & _motif_OPTION_MASK ? " alt" : "",
  2055. modifiers & _motif_SHIFT_MASK ? " shift" : "", message -> message == WM_KEYDOWN ? "keydown" : "syskeydown", kar);
  2056. }*/
  2057. /*
  2058. * Intercept accelerators, which may be function keys or Command combinations.
  2059. * Some Alt-GR combinations denote special characters (e.g. backslash) on some (e.g. German) keyboards;
  2060. * in such a case, the message is WM_KEYDOWN, and the reported modifier keys are Control (!) and Alt
  2061. * (on a German keyboard, the backslash can be generated by Ctrl-Alt-ringel-s as well);
  2062. * otherwise Alt keys give WM_SYSKEYDOWN messages.
  2063. */
  2064. if (message -> message == WM_KEYDOWN && (GetKeyState (VK_CONTROL) < 0 || ! (message -> lParam & (1<<29))) ||
  2065. message -> message == WM_SYSKEYDOWN && GetKeyState (VK_MENU) < 0
  2066. && (message -> lParam & (1<<29)) || // R&N 413: Alt key is pressed
  2067. message -> message == WM_SYSKEYDOWN && GetKeyState (VK_CONTROL) < 0)
  2068. {
  2069. int kar = LOWORD (message -> wParam);
  2070. GuiObject me = (GuiObject) GetWindowLongPtr (message -> hwnd, GWLP_USERDATA);
  2071. int modifiers = 0;
  2072. if (GetKeyState (VK_CONTROL) < 0) modifiers |= _motif_COMMAND_MASK;
  2073. if (GetKeyState (VK_MENU) < 0) modifiers |= _motif_OPTION_MASK;
  2074. if (GetKeyState (VK_SHIFT) < 0) modifiers |= _motif_SHIFT_MASK;
  2075. /*if(kar>=48)Melder_casual ("modifiers:%s%s%s\nmessage: %s\nkar: %d",
  2076. modifiers & _motif_COMMAND_MASK ? " control" : "",
  2077. modifiers & _motif_OPTION_MASK ? " alt" : "",
  2078. modifiers & _motif_SHIFT_MASK ? " shift" : "", message -> message == WM_KEYDOWN ? "keydown" : "syskeydown", kar);*/
  2079. if (me && my shell) {
  2080. uint32 acc = my shell -> motiff.shell.lowAccelerators [modifiers];
  2081. //if (kar != VK_CONTROL) Melder_casual ("%d %d", acc, kar);
  2082. if (kar < 48) {
  2083. if (kar == VK_BACK) { // shortcut or text
  2084. if (acc & 1 << GuiMenu_BACKSPACE) { win_processKeyboardEquivalent (my shell, GuiMenu_BACKSPACE, modifiers); return; }
  2085. } else if (kar == VK_TAB) { // shortcut or text
  2086. if (acc & 1 << GuiMenu_TAB) { win_processKeyboardEquivalent (my shell, GuiMenu_TAB, modifiers); return; }
  2087. } else if (kar == VK_RETURN) { // shortcut, default button, or text
  2088. //Melder_information (U"RETURN ", acc, U" def ", Melder_pointer (my shell -> defaultButton));
  2089. if (acc & 1 << GuiMenu_ENTER) { win_processKeyboardEquivalent (my shell, GuiMenu_ENTER, modifiers); return; }
  2090. else {
  2091. if (my shell -> defaultButton && _GuiWinButton_tryToHandleShortcutKey (my shell -> defaultButton)) return;
  2092. }
  2093. } else if (kar == VK_ESCAPE) { // shortcut or cancel button
  2094. if (acc & 1 << GuiMenu_ESCAPE) { win_processKeyboardEquivalent (my shell, GuiMenu_ESCAPE, modifiers); return; }
  2095. else {
  2096. if (my shell -> cancelButton && _GuiWinButton_tryToHandleShortcutKey (my shell -> cancelButton)) return;
  2097. }
  2098. return;
  2099. } else if (kar == VK_PRIOR) { // shortcut or text
  2100. if (acc & 1 << GuiMenu_PAGE_UP) { win_processKeyboardEquivalent (my shell, GuiMenu_PAGE_UP, modifiers); return; }
  2101. } else if (kar == VK_NEXT) { // shortcut or text
  2102. if (acc & 1 << GuiMenu_PAGE_DOWN) { win_processKeyboardEquivalent (my shell, GuiMenu_PAGE_DOWN, modifiers); return; }
  2103. } else if (kar == VK_HOME) { // shortcut or text
  2104. if (acc & 1 << GuiMenu_HOME) { win_processKeyboardEquivalent (my shell, GuiMenu_HOME, modifiers); return; }
  2105. } else if (kar == VK_END) { // shortcut or text
  2106. if (acc & 1 << GuiMenu_END) { win_processKeyboardEquivalent (my shell, GuiMenu_END, modifiers); return; }
  2107. } else if (kar == VK_LEFT) { // shortcut or text
  2108. if (acc & 1 << GuiMenu_LEFT_ARROW) { win_processKeyboardEquivalent (my shell, GuiMenu_LEFT_ARROW, modifiers); return; }
  2109. } else if (kar == VK_RIGHT) { // shortcut or text
  2110. if (acc & 1 << GuiMenu_RIGHT_ARROW) { win_processKeyboardEquivalent (my shell, GuiMenu_RIGHT_ARROW, modifiers); return; }
  2111. } else if (kar == VK_UP) { // shortcut or text
  2112. if (acc & 1 << GuiMenu_UP_ARROW) { win_processKeyboardEquivalent (my shell, GuiMenu_UP_ARROW, modifiers); return; }
  2113. } else if (kar == VK_DOWN) { // shortcut or text
  2114. if (acc & 1 << GuiMenu_DOWN_ARROW) { win_processKeyboardEquivalent (my shell, GuiMenu_DOWN_ARROW, modifiers); return; }
  2115. } else if (kar == VK_INSERT) { // shortcut
  2116. win_processKeyboardEquivalent (my shell, GuiMenu_INSERT, modifiers);
  2117. return;
  2118. } else if (kar == VK_DELETE) { // shortcut or text
  2119. if (acc & 1 << GuiMenu_DELETE) { win_processKeyboardEquivalent (my shell, GuiMenu_DELETE, modifiers); return; }
  2120. } else if (kar == VK_HELP) { // simulate Command-?
  2121. win_processKeyboardEquivalent (my shell, '?', modifiers | _motif_SHIFT_MASK);
  2122. return;
  2123. }
  2124. } else if (kar >= VK_F1 && kar <= VK_F12) { /* 112...123 */
  2125. /*
  2126. * She has pressed one of the F keys.
  2127. */
  2128. if (win_processKeyboardEquivalent (my shell, kar - VK_F1 + GuiMenu_F1, modifiers)) return;
  2129. /* Let windowProc handle Alt-F4 etc. */
  2130. /*
  2131. * If the Command key is pressed with a printable character, this is often a menu shortcut.
  2132. */
  2133. } else if (modifiers & _motif_COMMAND_MASK) {
  2134. if (MEMBER (me, Text) && (kar == 'X' || kar == 'C' || kar == 'V' || kar == 'Z')) {
  2135. ; // let window proc handle text editing
  2136. } else if (kar >= 186) {
  2137. int shift = modifiers & _motif_SHIFT_MASK;
  2138. /*
  2139. * BUG: The following is not internationally correct.
  2140. */
  2141. if (kar == 186 && win_processKeyboardEquivalent (my shell, shift ? ':' : ';', modifiers) ||
  2142. kar == 187 && win_processKeyboardEquivalent (my shell, shift ? '+' : '=', modifiers) ||
  2143. kar == 188 && win_processKeyboardEquivalent (my shell, shift ? '<' : ',', modifiers) ||
  2144. kar == 189 && win_processKeyboardEquivalent (my shell, shift ? '_' : '-', modifiers) ||
  2145. kar == 190 && win_processKeyboardEquivalent (my shell, shift ? '>' : '.', modifiers) ||
  2146. kar == 191 && win_processKeyboardEquivalent (my shell, shift ? '?' : '/', modifiers) ||
  2147. kar == 192 && win_processKeyboardEquivalent (my shell, shift ? '~' : '`', modifiers) ||
  2148. kar == 219 && win_processKeyboardEquivalent (my shell, shift ? '{' : '[', modifiers) || // Alt-GR-ringel-s is here
  2149. kar == 220 && win_processKeyboardEquivalent (my shell, shift ? '|' : '\\', modifiers) ||
  2150. kar == 221 && win_processKeyboardEquivalent (my shell, shift ? '}' : ']', modifiers) ||
  2151. kar == 222 && win_processKeyboardEquivalent (my shell, shift ? '\"' : '\'', modifiers))
  2152. {
  2153. return;
  2154. }
  2155. } else {
  2156. if (win_processKeyboardEquivalent (my shell, kar, modifiers)) return; // handle shortcuts like Ctrl-T and Ctrl-Alt-T
  2157. /* Let window proc handle international Alt-GR (= Ctrl-Alt) sequences, which are plain characters. */
  2158. }
  2159. }
  2160. /* Other characters: to text. */
  2161. }
  2162. /* Not me or not my shell: let windowProc handle. */
  2163. } else if (message -> message == WM_CHAR) {
  2164. int kar = LOWORD (message -> wParam);
  2165. /*
  2166. * Catch character messages to push buttons and toggle buttons:
  2167. * divert them to a drawing area, if possible.
  2168. */
  2169. GuiObject me = (GuiObject) GetWindowLongPtr (message -> hwnd, GWLP_USERDATA);
  2170. if (me && MEMBER2 (me, PushButton, ToggleButton)) {
  2171. GuiObject drawingArea = _motif_findDrawingArea (my shell);
  2172. if (drawingArea) {
  2173. _GuiWinDrawingArea_handleKey (drawingArea, kar); // TODO: event -> key?
  2174. return;
  2175. }
  2176. }
  2177. /*
  2178. * Next, try tab navigation.
  2179. */
  2180. if (me && MEMBER (me, Text) && kar == 9) {
  2181. trace (U"Tab navigation with shell ", Melder_pointer (my shell), U" and from text widget ", Melder_pointer (me));
  2182. GuiObject nextTextWidget = _motif_getNextTextWidget (my shell, me, GetKeyState (VK_SHIFT) < 0);
  2183. trace (U"Tab navigation to text widget ", Melder_pointer (nextTextWidget));
  2184. if (nextTextWidget != NULL) {
  2185. _GuiText_setTheTextFocus (nextTextWidget);
  2186. GuiText_setSelection ((GuiText) nextTextWidget -> userData, 0, 10000000);
  2187. return;
  2188. }
  2189. }
  2190. } else if (message -> message == WM_LBUTTONDOWN) {
  2191. /*
  2192. * Catch mouse-down messages to cascade buttons:
  2193. * post the associated menu, if any.
  2194. */
  2195. GuiObject me = (GuiObject) GetWindowLongPtr (message -> hwnd, GWLP_USERDATA);
  2196. //Melder_information (Melder_pointer (me), U" -- ", Melder_pointer (my subMenuId));
  2197. if (me && MEMBER (me, CascadeButton) && my subMenuId) {
  2198. RECT rect;
  2199. GetWindowRect (my window, & rect);
  2200. TrackPopupMenu (my subMenuId -> nat.menu.handle, TPM_LEFTBUTTON |
  2201. TPM_LEFTALIGN | TPM_TOPALIGN, rect.left, rect.bottom - 3, 0, my parent -> window, NULL);
  2202. return;
  2203. }
  2204. }
  2205. TranslateMessage (xevent); // Generate WM_CHAR messages.
  2206. DispatchMessage (xevent);
  2207. }
  2208. void GuiMainLoop () {
  2209. for (;;) {
  2210. XEvent event;
  2211. GuiNextEvent (& event);
  2212. XtDispatchEvent (& event);
  2213. }
  2214. }
  2215. #define main wingwmain
  2216. extern int main (int argc, char *argv []);
  2217. int APIENTRY WinMain (HINSTANCE instance, HINSTANCE /*previousInstance*/, LPSTR commandLine, int commandShow) {
  2218. theGui.instance = instance;
  2219. theGui.commandShow = commandShow;
  2220. int argc;
  2221. WCHAR** argvW = CommandLineToArgvW (GetCommandLineW (), & argc);
  2222. char** argv = Melder_malloc (char*, argc);
  2223. for (int iarg = 0; iarg < argc; iarg ++) {
  2224. argv [iarg] = Melder_32to8 (Melder_peekWto32 (argvW [iarg])).transfer();
  2225. }
  2226. return main (argc, argv);
  2227. }
  2228. static void on_close (HWND window) {
  2229. GuiObject me = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2230. if (me) {
  2231. if (my widgetClass == xmShellWidgetClass) {
  2232. int deleteResponse = my deleteResponse; // save this, in case the callback should kill the widget (XmDO_NOTHING)
  2233. GuiObject parent = my parent;
  2234. if (my motiff.shell.goAwayCallback)
  2235. my motiff.shell.goAwayCallback (me, my motiff.shell.goAwayClosure, NULL);
  2236. if (deleteResponse == XmDESTROY) {
  2237. XtDestroyWidget (me);
  2238. } else if (deleteResponse == XmUNMAP) {
  2239. /*
  2240. Unmapping is not the same as unmanaging.
  2241. One of the oldest bugs in Praat was that in the Windows edition
  2242. the Picture window could not be closed a second time,
  2243. and that after it had been closed and opened the scroll bars did not move when
  2244. the user resized the window.
  2245. The solution was to use only some of the code from XtUnmanageChild here,
  2246. without clearing the 'managed' attribute.
  2247. */
  2248. _GuiText_handleFocusLoss (my textFocus);
  2249. ShowWindow (my window, SW_HIDE);
  2250. }
  2251. return;
  2252. } else FORWARD_WM_CLOSE (window, DefWindowProc);
  2253. } else FORWARD_WM_CLOSE (window, DefWindowProc);
  2254. }
  2255. static GuiObject findItem (GuiObject menu, int id) {
  2256. GuiObject child = menu -> firstChild;
  2257. for (child = menu -> firstChild; child != NULL; child = child -> nextSibling) {
  2258. if (child -> widgetClass == xmPulldownMenuWidgetClass) {
  2259. GuiObject result = findItem (child, id);
  2260. if (result) return result;
  2261. } else {
  2262. Melder_assert (MEMBER4 (child, PushButton, ToggleButton, CascadeButton, Separator));
  2263. if (child -> nat.entry.id == id) return child;
  2264. }
  2265. }
  2266. return NULL;
  2267. }
  2268. static void on_command (HWND window, int id, HWND controlWindow, UINT codeNotify) {
  2269. GuiObject parent = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2270. if (parent) {
  2271. if (controlWindow) {
  2272. GuiObject control = (GuiObject) GetWindowLongPtr (controlWindow, GWLP_USERDATA);
  2273. if (control) {
  2274. switch (control -> widgetClass) {
  2275. /*
  2276. * Clicking on a button takes away the focus from a text widget.
  2277. * We have already been notified of that by EN_KILLFOCUS.
  2278. * We have to put the focus back (see GuiText.c, parenthesized discussion 1.4);
  2279. * this has to be done before calling the callbacks, because these may destroy or hide the text widget.
  2280. */
  2281. case xmPushButtonWidgetClass:
  2282. /*
  2283. * If EN_KILLFOCUS had not cleared the global text focus,
  2284. * the following message would not actually do SetFocus!
  2285. */
  2286. _GuiText_setTheTextFocus (control -> shell -> textFocus);
  2287. _GuiWinButton_handleClick (control);
  2288. break;
  2289. case xmToggleButtonWidgetClass:
  2290. _GuiText_setTheTextFocus (control -> shell -> textFocus);
  2291. if (control -> isRadioButton) {
  2292. _GuiWinRadioButton_handleClick (control);
  2293. } else {
  2294. _GuiWinCheckButton_handleClick (control);
  2295. }
  2296. break;
  2297. case xmListWidgetClass:
  2298. if (codeNotify == LBN_SELCHANGE) {
  2299. _GuiWinList_handleClick (control);
  2300. } else FORWARD_WM_COMMAND (window, id, controlWindow, codeNotify, DefWindowProc);
  2301. break;
  2302. case xmTextWidgetClass:
  2303. if (codeNotify == EN_CHANGE) {
  2304. _GuiText_handleValueChanged (control);
  2305. } else if (codeNotify == EN_SETFOCUS) {
  2306. _GuiText_handleFocusReception (control);
  2307. } else if (codeNotify == EN_KILLFOCUS) {
  2308. _GuiText_handleFocusLoss (control); // for button clicks (see above)
  2309. }
  2310. break;
  2311. default: break;
  2312. }
  2313. } else FORWARD_WM_COMMAND (window, id, controlWindow, codeNotify, DefWindowProc);
  2314. } else { // menu choice
  2315. GuiObject menuBar = NULL;
  2316. if (MEMBER (parent, Shell))
  2317. menuBar = parent -> nat.shell.menuBar;
  2318. else if (MEMBER (parent, RowColumn))
  2319. menuBar = parent;
  2320. if (menuBar) {
  2321. GuiObject item = findItem (menuBar, id);
  2322. if (item) {
  2323. if (item -> widgetClass == xmPushButtonWidgetClass) {
  2324. if (item -> activateCallback)
  2325. item -> activateCallback (item, item -> activateClosure, 0);
  2326. } else if (item -> widgetClass == xmToggleButtonWidgetClass) {
  2327. XmToggleButtonGadgetSetState (item, 1 - XmToggleButtonGadgetGetState (item), False);
  2328. _Gui_callCallbacks (item, & item -> motiff.toggleButton.valueChangedCallbacks, 0);
  2329. }
  2330. }
  2331. }
  2332. }
  2333. } else FORWARD_WM_COMMAND (window, id, controlWindow, codeNotify, DefWindowProc);
  2334. }
  2335. static void on_destroy (HWND window) {
  2336. (void) window;
  2337. }
  2338. static void on_lbuttonDown (HWND window, BOOL doubleClick, int x, int y, UINT flags) {
  2339. GuiObject me = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2340. if (me) {
  2341. if (MEMBER (me, DrawingArea)) {
  2342. _GuiWinDrawingArea_handleClick (me, x, y);
  2343. } else FORWARD_WM_LBUTTONDOWN (window, doubleClick, x, y, flags, DefWindowProc);
  2344. } else FORWARD_WM_LBUTTONDOWN (window, doubleClick, x, y, flags, DefWindowProc);
  2345. }
  2346. static void on_paint (HWND window) {
  2347. GuiObject me = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2348. if (me) {
  2349. if (my widgetClass == xmDrawingAreaWidgetClass) {
  2350. _GuiWinDrawingArea_update (me);
  2351. } else FORWARD_WM_PAINT (window, DefWindowProc);
  2352. } else FORWARD_WM_PAINT (window, DefWindowProc);
  2353. }
  2354. static void on_hscroll (HWND window, HWND controlWindow, UINT code, int pos) {
  2355. GuiObject parent = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2356. if (parent) {
  2357. GuiObject control = (GuiObject) GetWindowLongPtr (controlWindow, GWLP_USERDATA);
  2358. if (control) {
  2359. on_scroll (control, code, pos);
  2360. } else FORWARD_WM_HSCROLL (window, controlWindow, code, pos, DefWindowProc);
  2361. } else FORWARD_WM_HSCROLL (window, controlWindow, code, pos, DefWindowProc);
  2362. }
  2363. static void on_vscroll (HWND window, HWND controlWindow, UINT code, int pos) {
  2364. GuiObject parent = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2365. if (parent) {
  2366. GuiObject control = (GuiObject) GetWindowLongPtr (controlWindow, GWLP_USERDATA);
  2367. if (control) {
  2368. on_scroll (control, code, pos);
  2369. } else FORWARD_WM_VSCROLL (window, controlWindow, code, pos, DefWindowProc);
  2370. } else FORWARD_WM_VSCROLL (window, controlWindow, code, pos, DefWindowProc);
  2371. }
  2372. static void on_size (HWND window, UINT state, int cx, int cy) {
  2373. GuiObject me = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2374. if (me && MEMBER (me, Shell) && (state == SIZE_RESTORED || state == SIZE_MAXIMIZED)) {
  2375. int oldWidth = my width, oldHeight = my height;
  2376. int newWidth = cx;
  2377. int newHeight = cy;
  2378. my width = newWidth;
  2379. my height = newHeight;
  2380. FORWARD_WM_SIZE (window, state, cx, cy, DefWindowProc);
  2381. if (! my managed) {
  2382. } else if (my nat.shell.duringMoveWindow) // Yeah, a BUG fix. Only react to user actions.
  2383. my nat.shell.duringMoveWindow = False;
  2384. else if (newWidth != oldWidth || newHeight != oldHeight) {
  2385. shellResizeWidget (me, 0, 0, newWidth - oldWidth, newHeight - oldHeight);
  2386. }
  2387. } else FORWARD_WM_SIZE (window, state, cx, cy, DefWindowProc);
  2388. }
  2389. static void on_key (HWND window, UINT key, BOOL down, int repeat, UINT flags) {
  2390. Melder_assert (down == true);
  2391. GuiObject me = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2392. if (me && key >= VK_LEFT && key <= VK_DOWN) {
  2393. //Melder_warning (U"Widget type ", my widgetClass);
  2394. if (MEMBER (me, Shell)) {
  2395. GuiObject drawingArea = _motif_findDrawingArea (me);
  2396. if (drawingArea) {
  2397. GuiObject textFocus = drawingArea -> shell -> textFocus; // BUG: ignore?
  2398. _GuiWinDrawingArea_handleKey (drawingArea, key);
  2399. } else {
  2400. FORWARD_WM_KEYDOWN (window, key, repeat, flags, DefWindowProc);
  2401. }
  2402. } else FORWARD_WM_KEYDOWN (window, key, repeat, flags, DefWindowProc);
  2403. } else {
  2404. FORWARD_WM_KEYDOWN (window, key, repeat, flags, DefWindowProc);
  2405. }
  2406. }
  2407. static void on_char (HWND window, TCHAR kar, int repeat) {
  2408. GuiObject me = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2409. if (me) {
  2410. //Melder_warning (U"Widget type ", my widgetClass);
  2411. if (MEMBER (me, Shell)) {
  2412. GuiObject drawingArea = _motif_findDrawingArea (me);
  2413. if (drawingArea) {
  2414. GuiObject textFocus = drawingArea -> shell -> textFocus; // BUG: ignore?
  2415. _GuiWinDrawingArea_handleKey (drawingArea, kar);
  2416. } else {
  2417. FORWARD_WM_CHAR (window, kar, repeat, DefWindowProc);
  2418. }
  2419. } else FORWARD_WM_CHAR (window, kar, repeat, DefWindowProc);
  2420. } else {
  2421. FORWARD_WM_CHAR (window, kar, repeat, DefWindowProc);
  2422. }
  2423. }
  2424. static void on_move (HWND window, int x, int y) {
  2425. GuiObject me = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2426. /*if (me && MEMBER (me, Shell)) {
  2427. my x = x - ( my motiff.shell.isDialog ? GetSystemMetrics (SM_CXFIXEDFRAME) : GetSystemMetrics (SM_CXSIZEFRAME) );
  2428. my y = y - GetSystemMetrics (SM_CYCAPTION) - ( my motiff.shell.isDialog ? GetSystemMetrics (SM_CYFIXEDFRAME) :
  2429. GetSystemMetrics (SM_CYSIZEFRAME) + GetSystemMetrics (SM_CYMENU) );
  2430. }*/
  2431. FORWARD_WM_MOVE (window, x, y, DefWindowProc);
  2432. }
  2433. static HBRUSH on_ctlColorStatic (HWND window, HDC hdc, HWND controlWindow, int type) {
  2434. GuiObject parent = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2435. (void) type;
  2436. if (parent) {
  2437. GuiObject control = (GuiObject) GetWindowLongPtr (controlWindow, GWLP_USERDATA);
  2438. if (control) {
  2439. SetBkMode (hdc, TRANSPARENT);
  2440. return GetStockBrush (LTGRAY_BRUSH);
  2441. }
  2442. }
  2443. return FORWARD_WM_CTLCOLORSTATIC (window, hdc, controlWindow, DefWindowProc);
  2444. }
  2445. static HBRUSH on_ctlColorBtn (HWND window, HDC hdc, HWND controlWindow, int type) {
  2446. GuiObject parent = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2447. (void) type;
  2448. if (parent) {
  2449. GuiObject control = (GuiObject) GetWindowLongPtr (controlWindow, GWLP_USERDATA);
  2450. if (control) {
  2451. SetBkMode (hdc, TRANSPARENT);
  2452. return GetStockBrush (LTGRAY_BRUSH);
  2453. }
  2454. }
  2455. return FORWARD_WM_CTLCOLORBTN (window, hdc, controlWindow, DefWindowProc);
  2456. }
  2457. static void on_activate (HWND window, UINT state, HWND hActive, BOOL minimized) {
  2458. GuiObject me = (GuiObject) GetWindowLongPtr (window, GWLP_USERDATA);
  2459. if (me && my widgetClass == xmShellWidgetClass) {
  2460. if (state == WA_INACTIVE || minimized) {
  2461. _GuiText_handleFocusLoss (my textFocus);
  2462. } else {
  2463. _GuiText_setTheTextFocus (my textFocus);
  2464. }
  2465. return;
  2466. } else FORWARD_WM_ACTIVATE (window, state, hActive, minimized, DefWindowProc);
  2467. }
  2468. static LRESULT CALLBACK windowProc (HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
  2469. switch (message) {
  2470. HANDLE_MSG (window, WM_CLOSE, on_close);
  2471. HANDLE_MSG (window, WM_COMMAND, on_command);
  2472. HANDLE_MSG (window, WM_DESTROY, on_destroy);
  2473. HANDLE_MSG (window, WM_LBUTTONDOWN, on_lbuttonDown);
  2474. HANDLE_MSG (window, WM_PAINT, on_paint);
  2475. HANDLE_MSG (window, WM_HSCROLL, on_hscroll);
  2476. HANDLE_MSG (window, WM_VSCROLL, on_vscroll);
  2477. HANDLE_MSG (window, WM_SIZE, on_size);
  2478. HANDLE_MSG (window, WM_KEYDOWN, on_key);
  2479. HANDLE_MSG (window, WM_CHAR, on_char);
  2480. HANDLE_MSG (window, WM_MOVE, on_move);
  2481. HANDLE_MSG (window, WM_CTLCOLORBTN, on_ctlColorBtn);
  2482. HANDLE_MSG (window, WM_CTLCOLORSTATIC, on_ctlColorStatic);
  2483. HANDLE_MSG (window, WM_ACTIVATE, on_activate);
  2484. case WM_USER: {
  2485. /*if (IsIconic (window)) ShowWindow (window, SW_RESTORE);
  2486. SetForegroundWindow (window);*/
  2487. return theUserMessageCallback ? theUserMessageCallback () : 1;
  2488. }
  2489. default: return DefWindowProc (window, message, wParam, lParam);
  2490. }
  2491. }
  2492. bool motif_win_mouseStillDown () {
  2493. XEvent event;
  2494. if (! GetCapture ()) SetCapture (theApplicationShell -> window);
  2495. if (PeekMessage (& event, 0, 0, 0, PM_REMOVE)) {
  2496. if (event. message == WM_LBUTTONUP) {
  2497. DispatchMessage (& event);
  2498. ReleaseCapture ();
  2499. return false;
  2500. }
  2501. }
  2502. return true;
  2503. }
  2504. void motif_win_setUserMessageCallback (int (*userMessageCallback) (void)) {
  2505. theUserMessageCallback = userMessageCallback;
  2506. }
  2507. void Gui_setOpenDocumentCallback (void (*openDocumentCallback) (MelderFile file)) {
  2508. theOpenDocumentCallback = openDocumentCallback;
  2509. }
  2510. #endif
  2511. void Gui_setQuitApplicationCallback (int (*quitApplicationCallback) (void)) {
  2512. theQuitApplicationCallback = quitApplicationCallback;
  2513. }
  2514. /* End of file motifEmulator.cpp */