XEmacs 21.2.28 "Hermes".
[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 #if defined (DEFINE_TOP_LEVEL_EMACS_SHELL)
94 #define EMACS_SHELL_WIDGET TopLevelEmacsShellWidget
95 #define SUPERCLASS_WIDGET_CLASS topLevelShellWidgetClass
96 #define SUPERCLASS_CLASS_REC topLevelShellClassRec
97 #define EMACS_SHELL_REC TopLevelEmacsShellRec
98 #define EMACS_SHELL_CLASS_REC topLevelEmacsShellClassRec
99 #define EMACS_SHELL_CLASS_REC_TYPE TopLevelEmacsShellClassRec
100 #define EMACS_SHELL_CLASS_NAME "TopLevelEmacsShell"
101 #define EMACS_SHELL_WIDGET_CLASS topLevelEmacsShellWidgetClass
102 #define EMACS_SHELL_UPDATE_SIZE_HINTS TopLevelEmacsShellUpdateSizeHints
103 #elif defined (DEFINE_TRANSIENT_EMACS_SHELL)
104 #define EMACS_SHELL_WIDGET TransientEmacsShellWidget
105 #define SUPERCLASS_WIDGET_CLASS transientShellWidgetClass
106 #define SUPERCLASS_CLASS_REC transientShellClassRec
107 #define EMACS_SHELL_REC TransientEmacsShellRec
108 #define EMACS_SHELL_CLASS_REC transientEmacsShellClassRec
109 #define EMACS_SHELL_CLASS_REC_TYPE TransientEmacsShellClassRec
110 #define EMACS_SHELL_CLASS_NAME "TransientEmacsShell"
111 #define EMACS_SHELL_WIDGET_CLASS transientEmacsShellWidgetClass
112 #define EMACS_SHELL_UPDATE_SIZE_HINTS TransientEmacsShellUpdateSizeHints
113 #else
114 Error.  Must define either DEFINE_TOP_LEVEL_EMACS_SHELL or
115 DEFINE_TRANSIENT_EMACS_SHELL.
116 #endif
117
118 typedef struct {
119     XtPointer           next_extension;
120     XrmQuark            record_type;
121     long                version;
122     Cardinal            record_size;
123 } GenericClassExtRec;
124
125 static XtGeometryResult RootGeometryManager (Widget gw,
126   XtWidgetGeometry *request, XtWidgetGeometry *reply);
127 static void ChangeManaged (Widget w);
128
129 /* snarfed from Shell.c */
130 #define BIGSIZE ((Dimension)32767)
131
132 static XtResource resources[] = {
133 #define offset(field) XtOffset(EMACS_SHELL_WIDGET, emacs_shell.field)
134 #define coreoffset(field) XtOffset(EMACS_SHELL_WIDGET, core.field)
135 #ifdef LWLIB_USES_MOTIF
136   /* *** BOGOSITY^10! *** The Motif VendorShell fucks around with
137      the default values for X and Y, for no obvious reason.  This
138      causes Shell to indicate that the defaults of (0,0) were
139      program-specified, instead of letting the WM do what it wants. */
140   { XtNx, XtCPosition,
141     XtRPosition, sizeof (Position),
142     coreoffset (x), XtRImmediate, (XtPointer)BIGSIZE },
143   { XtNy, XtCPosition,
144     XtRPosition, sizeof (Position),
145     coreoffset (y), XtRImmediate, (XtPointer)BIGSIZE },
146 #endif
147   { XtNwidthCells, XtCWidthCells,
148     XtRInt, sizeof (int),
149     offset (width_cells), XtRImmediate, (XtPointer)0 },
150   { XtNheightCells, XtCHeightCells,
151     XtRInt, sizeof (int),
152     offset (height_cells), XtRImmediate, (XtPointer)0 },
153   { XtNminWidthCells, XtCMinWidthCells,
154     XtRInt, sizeof (int),
155     offset (min_width_cells), XtRImmediate, (XtPointer)0 },
156   { XtNminHeightCells, XtCMinHeightCells,
157     XtRInt, sizeof (int),
158     offset (min_height_cells), XtRImmediate, (XtPointer)0 },
159 };
160
161 static CompositeClassExtensionRec compositeClassExtRec = {
162     NULL,
163     NULLQUARK,
164     XtCompositeExtensionVersion,
165     sizeof (CompositeClassExtensionRec),
166     TRUE,
167 };
168
169 static ShellClassExtensionRec shellClassExtRec = {
170     NULL,
171     NULLQUARK,
172     XtShellExtensionVersion,
173     sizeof (ShellClassExtensionRec),
174     RootGeometryManager
175 };
176
177 EMACS_SHELL_CLASS_REC_TYPE EMACS_SHELL_CLASS_REC = {
178     { /*
179        *        core_class fields
180        */
181     /* superclass         */    (WidgetClass) &SUPERCLASS_CLASS_REC,
182     /* class_name         */    (String) EMACS_SHELL_CLASS_NAME,
183     /* size               */    sizeof (EMACS_SHELL_REC),
184     /* Class Initializer  */    NULL,
185     /* class_part_initialize*/  NULL, /* XtInheritClassPartInitialize, */
186     /* Class init'ed ?    */    FALSE,
187     /* initialize         */    NULL,
188     /* initialize_notify  */    NULL,
189     /* realize            */    XtInheritRealize,
190     /* actions            */    NULL,
191     /* num_actions        */    0,
192     /* resources          */    resources,
193     /* resource_count     */    XtNumber (resources),
194     /* xrm_class          */    NULLQUARK,
195     /* compress_motion    */    FALSE,
196     /* compress_exposure  */    TRUE,
197     /* compress_enterleave*/    FALSE,
198     /* visible_interest   */    TRUE,
199     /* destroy            */    NULL,
200     /* resize             */    XtInheritResize,
201     /* expose             */    NULL,
202     /* set_values         */    NULL, /* XtInheritSetValues, */
203     /* set_values_hook    */    NULL,
204     /* set_values_almost  */    XtInheritSetValuesAlmost,
205     /* get_values_hook    */    NULL,
206     /* accept_focus       */    NULL,
207     /* intrinsics version */    XtVersion,
208     /* callback offsets   */    NULL,
209     /* tm_table           */    NULL,
210     /* query_geometry     */    NULL,
211     /* display_accelerator*/    NULL,
212     /* extension          */    NULL
213   },{ /* Composite */
214     /* geometry_manager   */    XtInheritGeometryManager,
215     /* change_managed     */    ChangeManaged,
216     /* insert_child       */    XtInheritInsertChild,
217     /* delete_child       */    XtInheritDeleteChild,
218     /* extension          */    (XtPointer)&compositeClassExtRec
219   },{ /* Shell */
220     /* extension          */    (XtPointer)&shellClassExtRec
221   },{ /* WMShell */
222     /* extension          */    NULL
223   },{ /* VendorShell */
224     /* extension          */    NULL
225   },{ /* TopLevelShell or TransientShell */
226       /* both have exactly one XtPointer here. */
227     /* extension          */    NULL
228   },{ /* EmacsShell */
229     0
230   }
231 };
232
233 WidgetClass EMACS_SHELL_WIDGET_CLASS = (WidgetClass) &EMACS_SHELL_CLASS_REC;
234
235 static void
236 update_size_hints_internal (EMACS_SHELL_WIDGET w,
237                             int width, int height)
238 {
239   int base_width, base_height;
240   int cell_width, cell_height;
241   Arg al [10];
242
243   /* time to update them thar size hints */
244   cell_width = w->wm.size_hints.width_inc;
245   cell_height = w->wm.size_hints.height_inc;
246   base_width = width - cell_width * w->emacs_shell.width_cells;
247   base_height = height - cell_height * w->emacs_shell.height_cells;
248 #ifdef DEBUG_GEOMETRY_MANAGEMENT
249   /* Very useful info when debugging geometry management problems.
250      When it's guaranteed that no more such problems exist, take
251      this stuff out. */
252   printf ("update_size_hints_internal:\n");
253   printf ("  actual pixel size: %d %d\n", width, height);
254   printf ("  cell size in pixels: %d %d\n", cell_width, cell_height);
255   printf ("  text area size in cells: %d %d\n", w->emacs_shell.width_cells,
256          w->emacs_shell.height_cells);
257   printf ("  base size set to: %d %d\n", base_width, base_height);
258   fflush (stdout);
259 #endif
260   XtSetArg(al [0], XtNbaseWidth, base_width);
261   XtSetArg(al [1], XtNbaseHeight, base_height);
262   XtSetArg(al [2], XtNminWidth, base_width +
263            cell_width * w->emacs_shell.min_width_cells);
264   XtSetArg(al [3], XtNminHeight, base_height +
265            cell_height * w->emacs_shell.min_height_cells);
266   XtSetValues ((Widget) w, al, 4);
267 }
268
269 static XtGeometryResult
270 SuperClassRootGeometryManager (Widget gw,
271                                XtWidgetGeometry *request,
272                                XtWidgetGeometry *reply)
273 {
274   ShellWidgetClass swc = (ShellWidgetClass) SUPERCLASS_WIDGET_CLASS;
275   ShellClassExtensionRec *scer;
276   GenericClassExtRec *gcer;
277
278   /* find the shell extension record that specifies the
279      root geometry manager method */
280   for (gcer = (GenericClassExtRec *) swc->shell_class.extension;
281        gcer;
282        gcer = (GenericClassExtRec *) gcer->next_extension)
283     {
284       if (gcer->record_type == NULLQUARK)
285         break;
286     }
287
288   if (!gcer)
289     abort ();
290
291   /* call it to actually make the geometry request */
292   scer = (ShellClassExtensionRec *) gcer;
293   return (scer->root_geometry_manager)(gw, request, reply);
294 }
295
296 static XtGeometryResult
297 RootGeometryManager (Widget gw,
298                      XtWidgetGeometry *request,
299                      XtWidgetGeometry *reply)
300 {
301   EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) gw;
302   /* OK since this file is not dumped */
303   static int reentrant = 0;
304   XtGeometryResult result;
305
306   if (reentrant)
307     abort ();
308   reentrant++;
309
310 #ifdef DEBUG_GEOMETRY_MANAGEMENT
311   printf ("root_geometry_manager:\n");
312   printf ("  current shell size: %d %d\n", w->core.width, w->core.height);
313   if (request->request_mode & CWWidth)
314     printf ("width requested;");
315   if (request->request_mode & CWHeight)
316     printf ("height requested;");
317   printf ("\n");
318   printf ("  requested shell size: %d %d\n", request->width, request->height);
319 #endif
320   /* update the size hints */
321   update_size_hints_internal (w,
322                               request->request_mode & CWWidth ?
323                               request->width : w->core.width,
324                               request->request_mode & CWHeight ?
325                               request->height : w->core.height);
326
327   result = SuperClassRootGeometryManager (gw, request, reply);
328
329 #ifdef DEBUG_GEOMETRY_MANAGEMENT
330   printf ("  result: %s\n",
331           result == XtGeometryYes ? "XtGeometryYes" :
332           result == XtGeometryNo ? "XtGeometryNo" :
333           result == XtGeometryAlmost ? "XtGeometryAlmost" :
334           "XtGeometryDone");
335   if (reply->request_mode & CWWidth)
336     printf ("width returned;");
337   if (reply->request_mode & CWHeight)
338     printf ("height returned;");
339   printf ("\n");
340   printf ("  resulting shell size: %d %d\n", reply->width, reply->height);
341   printf ("----------\n");
342   fflush (stdout);
343 #endif
344   reentrant--;
345   return result;
346 }
347
348 static void
349 ChangeManaged (Widget wid)
350 {
351   EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) wid;
352
353   /* If not realized, then we're being called from XtRealizeWidget().
354      RootGeometryManager() has not yet been called, and thus our
355      base size is incorrect.  We need to set it now or the Shell
356      will mess up geometry specifications with negative positional
357      offsets. */
358   if (!XtIsRealized (wid))
359     {
360       Widget child = NULL;
361       Cardinal i;
362
363       /* the managed child indicates what our size is */
364       for (i = 0; i < w->composite.num_children; i++) {
365         if (XtIsManaged(w->composite.children[i])) {
366           child = w->composite.children[i];
367           break;
368         }
369       }
370
371       update_size_hints_internal (w, child->core.width, child->core.height);
372     }
373
374   /* call the real ChangeManaged */
375   (((ShellWidgetClass) SUPERCLASS_WIDGET_CLASS)->
376    composite_class.change_managed)(wid);
377 }
378
379
380 /******************* external entry points *********************/
381
382 void
383 EMACS_SHELL_UPDATE_SIZE_HINTS (Widget gw)
384 {
385   EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) gw;
386   update_size_hints_internal (w, w->core.width, w->core.height);
387 }