XEmacs 21.4.20 "Double Solitaire".
[chise/xemacs-chise.git.1] / src / EmacsShell-sub.c
1 /* Emacs shell widget -- define the two widgets.
2    Copyright (C) 1994, 1995 Sun Microsystems, Inc.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING.  If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 /* Synched up with: Not in FSF. */
22
23 /* Written by Ben Wing, May, 1994. */
24
25 /*
26    It is rather unfortunate that we have to do this.  Blame those
27    short-sighted people who designed the monstrosities known as
28    Xt and ICCCM.
29 */
30
31 /*
32    This widget is not actually Emacs-specific; perhaps there could
33    be a better name than "EmacsShell".  What it does is work around
34    a limitation in Xt in correctly dealing with the window-manager
35    size hints with applications that
36
37    (a) dynamically change their window size
38    (b) have a cell size (width-inc and height-inc) other than 1
39
40    and
41
42    (c) cannot predict in advance exactly what size their shell will be
43        (This is the more common situation, when you have a number
44        of widgets, each with their own size ideas)
45
46    This widget assumes that your program contains a fixed "base size"
47    plus some number of cells (e.g. character cells).  The WMShell
48    resources "widthInc" and "heightInc" specify the size of a
49    character cell, and the window manager will report the app's
50    size in cells rather than in pixels.
51
52    If you use this widget, do not use the WMShell resources
53    "baseWidth", "baseHeight", "minWidth", or "minHeight".
54    Instead, use "widthCells" and "heightCells" to specify the
55    current size in cells (you must keep this up-to-date),
56    and "minWidthCells" and "minHeightCells" to specify the
57    minimum size in cells.
58
59    Every time that the program issues a size command, the
60    "baseWidth", "baseHeight", "minWidth", and "minHeight" fields
61    of the WM_NORMAL_HINTS property will be updated to stay in
62    line with the resource values specified above.  The calculations
63    are done once the desired shell size is known but before the
64    window-manager size-change request is issued. (We must do it
65    at this time because before then we don't know what size we
66    will request, and after the request the deed has already
67    been done.)
68
69    After you change the "baseWidth", "baseHeight", "minWidth",
70    or "minHeight" resources, you need to call
71    EmacsShellUpdateSizeHints() to manually update the size
72    hints, except in the following two circumstances:
73
74    (a) you are about to make a geometry request.
75    (b) you are changing only "baseWidth" and "baseHeight"
76        from within a resize procedure.  (In this case,
77        the size hints are already correct.)
78
79 */
80
81 #include <config.h>
82
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <X11/StringDefs.h>
86 #include "xintrinsicp.h"
87 #include <X11/Shell.h>
88 #include <X11/ShellP.h>
89 #include <X11/Vendor.h>
90 #include <X11/VendorP.h>
91 #include "EmacsShellP.h"
92
93 #define ABORT abort
94
95 #if defined (DEFINE_TOP_LEVEL_EMACS_SHELL)
96 #define EMACS_SHELL_WIDGET TopLevelEmacsShellWidget
97 #define SUPERCLASS_WIDGET_CLASS topLevelShellWidgetClass
98 #define SUPERCLASS_CLASS_REC topLevelShellClassRec
99 #define EMACS_SHELL_REC TopLevelEmacsShellRec
100 #define EMACS_SHELL_CLASS_REC topLevelEmacsShellClassRec
101 #define EMACS_SHELL_CLASS_REC_TYPE TopLevelEmacsShellClassRec
102 #define EMACS_SHELL_CLASS_NAME "TopLevelEmacsShell"
103 #define EMACS_SHELL_WIDGET_CLASS topLevelEmacsShellWidgetClass
104 #define EMACS_SHELL_UPDATE_SIZE_HINTS TopLevelEmacsShellUpdateSizeHints
105 #elif defined (DEFINE_TRANSIENT_EMACS_SHELL)
106 #define EMACS_SHELL_WIDGET TransientEmacsShellWidget
107 #define SUPERCLASS_WIDGET_CLASS transientShellWidgetClass
108 #define SUPERCLASS_CLASS_REC transientShellClassRec
109 #define EMACS_SHELL_REC TransientEmacsShellRec
110 #define EMACS_SHELL_CLASS_REC transientEmacsShellClassRec
111 #define EMACS_SHELL_CLASS_REC_TYPE TransientEmacsShellClassRec
112 #define EMACS_SHELL_CLASS_NAME "TransientEmacsShell"
113 #define EMACS_SHELL_WIDGET_CLASS transientEmacsShellWidgetClass
114 #define EMACS_SHELL_UPDATE_SIZE_HINTS TransientEmacsShellUpdateSizeHints
115 #else
116 Error.  Must define either DEFINE_TOP_LEVEL_EMACS_SHELL or
117 DEFINE_TRANSIENT_EMACS_SHELL.
118 #endif
119
120 typedef struct {
121     XtPointer           next_extension;
122     XrmQuark            record_type;
123     long                version;
124     Cardinal            record_size;
125 } GenericClassExtRec;
126
127 static XtGeometryResult RootGeometryManager (Widget gw,
128   XtWidgetGeometry *request, XtWidgetGeometry *reply);
129 static void ChangeManaged (Widget w);
130
131 /* snarfed from Shell.c */
132 #define BIGSIZE ((Dimension)32767)
133
134 static XtResource resources[] = {
135 #define offset(field) XtOffset(EMACS_SHELL_WIDGET, emacs_shell.field)
136 #define coreoffset(field) XtOffset(EMACS_SHELL_WIDGET, core.field)
137 #ifdef LWLIB_USES_MOTIF
138   /* *** BOGOSITY^10! *** The Motif VendorShell fucks around with
139      the default values for X and Y, for no obvious reason.  This
140      causes Shell to indicate that the defaults of (0,0) were
141      program-specified, instead of letting the WM do what it wants. */
142   { XtNx, XtCPosition,
143     XtRPosition, sizeof (Position),
144     coreoffset (x), XtRImmediate, (XtPointer)BIGSIZE },
145   { XtNy, XtCPosition,
146     XtRPosition, sizeof (Position),
147     coreoffset (y), XtRImmediate, (XtPointer)BIGSIZE },
148 #endif
149   { XtNwidthCells, XtCWidthCells,
150     XtRInt, sizeof (int),
151     offset (width_cells), XtRImmediate, (XtPointer)0 },
152   { XtNheightCells, XtCHeightCells,
153     XtRInt, sizeof (int),
154     offset (height_cells), XtRImmediate, (XtPointer)0 },
155   { XtNminWidthCells, XtCMinWidthCells,
156     XtRInt, sizeof (int),
157     offset (min_width_cells), XtRImmediate, (XtPointer)0 },
158   { XtNminHeightCells, XtCMinHeightCells,
159     XtRInt, sizeof (int),
160     offset (min_height_cells), XtRImmediate, (XtPointer)0 },
161 };
162
163 static CompositeClassExtensionRec compositeClassExtRec = {
164     NULL,
165     NULLQUARK,
166     XtCompositeExtensionVersion,
167     sizeof (CompositeClassExtensionRec),
168     TRUE,
169 };
170
171 static ShellClassExtensionRec shellClassExtRec = {
172     NULL,
173     NULLQUARK,
174     XtShellExtensionVersion,
175     sizeof (ShellClassExtensionRec),
176     RootGeometryManager
177 };
178
179 EMACS_SHELL_CLASS_REC_TYPE EMACS_SHELL_CLASS_REC = {
180     { /*
181        *        core_class fields
182        */
183     /* superclass         */    (WidgetClass) &SUPERCLASS_CLASS_REC,
184     /* class_name         */    (String) EMACS_SHELL_CLASS_NAME,
185     /* size               */    sizeof (EMACS_SHELL_REC),
186     /* Class Initializer  */    NULL,
187     /* class_part_initialize*/  NULL, /* XtInheritClassPartInitialize, */
188     /* Class init'ed ?    */    FALSE,
189     /* initialize         */    NULL,
190     /* initialize_notify  */    NULL,
191     /* realize            */    XtInheritRealize,
192     /* actions            */    NULL,
193     /* num_actions        */    0,
194     /* resources          */    resources,
195     /* resource_count     */    XtNumber (resources),
196     /* xrm_class          */    NULLQUARK,
197     /* compress_motion    */    TRUE,
198     /* compress_exposure  */    XtExposeCompressMaximal | XtExposeNoRegion,
199     /* compress_enterleave*/    TRUE,
200     /* visible_interest   */    TRUE,
201     /* destroy            */    NULL,
202     /* resize             */    XtInheritResize,
203     /* expose             */    NULL,
204     /* set_values         */    NULL, /* XtInheritSetValues, */
205     /* set_values_hook    */    NULL,
206     /* set_values_almost  */    XtInheritSetValuesAlmost,
207     /* get_values_hook    */    NULL,
208     /* accept_focus       */    NULL,
209     /* intrinsics version */    XtVersion,
210     /* callback offsets   */    NULL,
211     /* tm_table           */    NULL,
212     /* query_geometry     */    NULL,
213     /* display_accelerator*/    NULL,
214     /* extension          */    NULL
215   },{ /* Composite */
216     /* geometry_manager   */    XtInheritGeometryManager,
217     /* change_managed     */    ChangeManaged,
218     /* insert_child       */    XtInheritInsertChild,
219     /* delete_child       */    XtInheritDeleteChild,
220     /* extension          */    (XtPointer)&compositeClassExtRec
221   },{ /* Shell */
222     /* extension          */    (XtPointer)&shellClassExtRec
223   },{ /* WMShell */
224     /* extension          */    NULL
225   },{ /* VendorShell */
226     /* extension          */    NULL
227   },{ /* TopLevelShell or TransientShell */
228       /* both have exactly one XtPointer here. */
229     /* extension          */    NULL
230   },{ /* EmacsShell */
231     0
232   }
233 };
234
235 WidgetClass EMACS_SHELL_WIDGET_CLASS = (WidgetClass) &EMACS_SHELL_CLASS_REC;
236
237 static void
238 update_size_hints_internal (EMACS_SHELL_WIDGET w,
239                             int width, int height)
240 {
241   int base_width, base_height;
242   int cell_width, cell_height;
243   Arg al [10];
244
245   /* time to update them thar size hints */
246   cell_width = w->wm.size_hints.width_inc;
247   cell_height = w->wm.size_hints.height_inc;
248   base_width = width - cell_width * w->emacs_shell.width_cells;
249   base_height = height - cell_height * w->emacs_shell.height_cells;
250 #ifdef DEBUG_GEOMETRY_MANAGEMENT
251   /* Very useful info when debugging geometry management problems.
252      When it's guaranteed that no more such problems exist, take
253      this stuff out. */
254   printf ("update_size_hints_internal:\n");
255   printf ("  actual pixel size: %d %d\n", width, height);
256   printf ("  cell size in pixels: %d %d\n", cell_width, cell_height);
257   printf ("  text area size in cells: %d %d\n", w->emacs_shell.width_cells,
258          w->emacs_shell.height_cells);
259   printf ("  base size set to: %d %d\n", base_width, base_height);
260   fflush (stdout);
261 #endif
262   XtSetArg(al [0], XtNbaseWidth, base_width);
263   XtSetArg(al [1], XtNbaseHeight, base_height);
264   XtSetArg(al [2], XtNminWidth, base_width +
265            cell_width * w->emacs_shell.min_width_cells);
266   XtSetArg(al [3], XtNminHeight, base_height +
267            cell_height * w->emacs_shell.min_height_cells);
268   XtSetValues ((Widget) w, al, 4);
269 }
270
271 static XtGeometryResult
272 SuperClassRootGeometryManager (Widget gw,
273                                XtWidgetGeometry *request,
274                                XtWidgetGeometry *reply)
275 {
276   ShellWidgetClass swc = (ShellWidgetClass) SUPERCLASS_WIDGET_CLASS;
277   ShellClassExtensionRec *scer;
278   GenericClassExtRec *gcer;
279
280   /* find the shell extension record that specifies the
281      root geometry manager method */
282   for (gcer = (GenericClassExtRec *) swc->shell_class.extension;
283        gcer;
284        gcer = (GenericClassExtRec *) gcer->next_extension)
285     {
286       if (gcer->record_type == NULLQUARK)
287         break;
288     }
289
290   if (!gcer)
291     ABORT ();
292
293   /* call it to actually make the geometry request */
294   scer = (ShellClassExtensionRec *) gcer;
295   return (scer->root_geometry_manager)(gw, request, reply);
296 }
297
298 static XtGeometryResult
299 RootGeometryManager (Widget gw,
300                      XtWidgetGeometry *request,
301                      XtWidgetGeometry *reply)
302 {
303   EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) gw;
304   /* OK since this file is not dumped */
305   static int reentrant = 0;
306   XtGeometryResult result;
307
308   if (reentrant)
309     ABORT ();
310   reentrant++;
311
312 #ifdef DEBUG_GEOMETRY_MANAGEMENT
313   printf ("root_geometry_manager:\n");
314   printf ("  current shell size: %d %d\n", w->core.width, w->core.height);
315   if (request->request_mode & CWWidth)
316     printf ("width requested;");
317   if (request->request_mode & CWHeight)
318     printf ("height requested;");
319   printf ("\n");
320   printf ("  requested shell size: %d %d\n", request->width, request->height);
321 #endif
322   /* update the size hints */
323   update_size_hints_internal (w,
324                               request->request_mode & CWWidth ?
325                               request->width : w->core.width,
326                               request->request_mode & CWHeight ?
327                               request->height : w->core.height);
328
329   result = SuperClassRootGeometryManager (gw, request, reply);
330
331 #ifdef DEBUG_GEOMETRY_MANAGEMENT
332   printf ("  result: %s\n",
333           result == XtGeometryYes ? "XtGeometryYes" :
334           result == XtGeometryNo ? "XtGeometryNo" :
335           result == XtGeometryAlmost ? "XtGeometryAlmost" :
336           "XtGeometryDone");
337   if (reply->request_mode & CWWidth)
338     printf ("width returned was %d%s",
339             reply->width,
340             reply->request_mode & CWHeight ? "; " : ".\n");
341   if (reply->request_mode & CWHeight)
342     printf ("height returned was %d.\n", reply->height);
343   /* #### does this also need to depend on the result?
344      With XtGeometryYes there doesn't seem to be a useful reply object. */
345   printf ("  resulting shell size: %d %d\n",
346           reply->request_mode & CWWidth ? reply->width : w->core.width,
347           reply->request_mode & CWHeight ? reply->height : w->core.height);
348   printf ("----------\n");
349   fflush (stdout);
350 #endif
351   reentrant--;
352   return result;
353 }
354
355 static void
356 ChangeManaged (Widget wid)
357 {
358   EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) wid;
359
360   /* If not realized, then we're being called from XtRealizeWidget().
361      RootGeometryManager() has not yet been called, and thus our
362      base size is incorrect.  We need to set it now or the Shell
363      will mess up geometry specifications with negative positional
364      offsets. */
365   if (!XtIsRealized (wid))
366     {
367       Widget child = NULL;
368       Cardinal i;
369
370       /* the managed child indicates what our size is */
371       for (i = 0; i < w->composite.num_children; i++) {
372         if (XtIsManaged(w->composite.children[i])) {
373           child = w->composite.children[i];
374           update_size_hints_internal (w, child->core.width,
375                                       child->core.height);
376           break;
377         }
378       }
379     }
380
381   /* call the real ChangeManaged */
382   (((ShellWidgetClass) SUPERCLASS_WIDGET_CLASS)->
383    composite_class.change_managed)(wid);
384 }
385
386
387 /******************* external entry points *********************/
388
389 void
390 EMACS_SHELL_UPDATE_SIZE_HINTS (Widget gw)
391 {
392   EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) gw;
393   update_size_hints_internal (w, w->core.width, w->core.height);
394 }