view lwlib/xlwmenu.c @ 80467:1d830d9c60e0

* textmodes/org-export-latex.el: New file. * textmodes/org-irc.el: New file. * textmodes/org-mac-message.el: New file. * textmodes/org-publish.el: New file. * textmodes/org.el (org-agenda-collect-markers) (org-create-marker-find-array) (org-check-agenda-marker-table, org-extract-log-state-settings) (org-nofm-to-completion, org-refile-goto-last-stored) (org-export-html-convert-special-strings, org-first-list-item-p) (org-imenu-new-marker, org-imenu-get-tree) (org-speedbar-set-agenda-restriction) (org-agenda-set-restriction-lock) (org-agenda-remove-restriction-lock) (org-agenda-maybe-redo, org-compute-latex-and-specials-regexp) (org-do-latex-and-special-faces, org-read-date-display) (org-select-remember-template, org-agenda-copy-local-variable) (org-read-date-get-relative, org-columns-cleanup-item) (org-find-entry-with-id) (org-insert-columns-dblock, org-listtable-to-string) (org-dblock-write:columnview, org-columns-capture-view) (org-edit-headline, org-parse-local-options) (org-in-clocktable-p, org-property-or-variable-value) (org-get-tags-string, org-up-heading-safe) (org-fast-todo-selection, org-get-wdays) (org-switch-to-buffer-other-window, org-agenda-deadline-face) (org-add-link-type, org-columns-content, org-columns-widen) (org-columns-next-allowed-value, org-columns-edit-allowed) (org-columns-store-format) (org-columns-uncompile-format, org-columns-redo) (org-columns-edit-attributes, org-delete-property) (org-set-property, org-columns-update, org-columns-compute) (org-columns-eval) (org-columns-not-in-agenda, org-columns-compute-all) (org-property-next-allowed-value) (org-columns-compile-format, org-fill-paragraph-experimental) (org-string-to-number, org-property-action, org-columns-move-left) (org-columns-new) (org-column-number-to-string, org-property-previous-allowed-value) (org-at-property-p, org-columns-delete) (org-columns-previous-allowed-value) (org-columns-move-right, org-columns-narrow) (org-property-get-allowed-values) (org-verify-version, org-column-string-to-number) (org-delete-property-globally) (org-get-property-block, org-entry-properties, org-entry-get) (org-entry-delete, org-entry-get-with-inheritance) (org-entry-put, org-buffer-property-keys, org-new-column-overlay) (org-overlay-columns) (org-overlay-columns-title, org-remove-column-overlays) (org-column-show-value, org-column-quit, org-column-edit) (org-cached-entry-get, org-table-put-field-property) (org-get-columns-autowidth-alist, turn-on-orgstruct) (orgstruct-error, orgstruct-setup) (orgstruct-make-binding, org-context-p, org-get-local-variables) (org-run-like-in-org-mode) (org-make-link-regexps, org-remove-keyword-keys) (org-get-todo-face, org-export-get-title-from-subtree) (org-assign-fast-keys, org-get-min-level) (org-get-wdays, org-clock-find-position) (org-clock-goto, org-fontify-priorities) (org-get-date-from-calendar) (org-entry-add-to-multivalued-property) (org-entry-remove-from-multivalued-property) (org-entry-member-in-multivalued-property) (org-entry-is-todo-p, org-entry-is-done-p, org-get-todo-state) (org-agenda-get-restriction-and-command) (org-open-link-from-string, org-columns-open-link) (org-go-to-remember-target, org-remember-insinuate) (org-icalendar-cleanup-string, org-first-sibling-p) (org-back-over-empty-lines, org-skip-whitespace) (org-goto-local-auto-isearch) (org-clocktable-steps, org-refile-get-location) (org-kill-line, org-remove-timestamp-with-keyword) (org-local-logging): New functions. (org-agenda-marker-table, org-log-note-how) (org-goto-local-auto-isearch-map) (org-last-remember-storage-locations) (org-imenu-markers, org-agenda-restriction-lock-overlay) (org-speedbar-restriction-lock-overlay) (org-massive-special-regexp) (org-verbatim-re, org-read-date-display-live) (org-agenda-file-regexp, org-complex-heading-regexp) (org-completion-fallback-command, org-todo-log-states) (org-goto-exit-command, org-goto-selected-point) (org-todo-key-alist, org-todo-key-trigger, org-agenda-remove-date) (org-agenda-after-show-hook, org-agenda-skip-unavailable-files) (org-fast-tag-selection-can-set-todo-state, org-local-properties) (org-store-link-functions, org-columns-top-level-marker) (orgstruct-mode-map, org-entry-property-inherited-from) (org-column-overlays, org-current-columns-fmt) (org-current-columns-maxwidths, org-column-map) (org-cached-props): New variables. (org-special-ctrl-k, org-goto-auto-isearch) (org-agenda-todo-keyword-format) (org-remember-use-refile-when-interactive) (org-imenu-depth, org-highlight-latex-fragments-and-specials) (org-agenda-skip-scheduled-if-done, org-extend-today-until) (org-agenda-window-frame-fractions, org-fontify-priorities) (org-agenda-deadline-leaders, org-use-property-inheritance) (org-clock-into-drawer, org-agenda-compact-blocks) (org-use-fast-todo-selection, org-fast-tag-selection-include-todo) (org-edit-timestamp-down-means-later, org-todo-keyword-faces) (org-agenda-deadline-faces, org-remember-store-without-prompt) (org-default-columns-format) (org-agenda-default-appointment-duration) (org-export-with-footnotes, org-clock-out-when-done) (org-global-properties, org-todo-keyword-faces) (org-archive-save-context-info) (org-edit-timestamp-down-means-later) (org-scheduled-past-days, org-export-with-drawers) (org-read-date-prefer-future, org-hide-emphasis-markers) (org-clock-heading-function, org-clock-in-switch-to-state) (org-export-with-special-strings): New options. (org-link-store, org-link-follow, org-latex) (org-remember-templates, org-time-stamp-rounding-minutes) (org-back-over-empty-lines, org-find-base-buffer-visiting) (org-columns-new): Fix typos in docstrings. (org-ctrl-c-star): Implement a missing branch in the decision tree. (org-select-remember-template): Clean the code. (org-prepare-dblock): Add the extra :content parameter. (org-write-agenda): New output type ".ics" files. (org-write-agenda): Call `org-icalendar-verify-function', both for time stamps and for TODO entries. (org-export-as-html): Revert the change that killed the html buffer. Side effects first need to be studied carefully. (org-get-tags-at): Fix the structure of the condition-case statement. (org-ts-regexp0, org-repeat-re, org-display-custom-time) (org-timestamp-change): Fix regulear expressions to swallow the extra character for repeat-shift control. (org-auto-repeat-maybe): Implement the new repeater mechanisms. (org-get-legal-level): Alias to `org-get-valid-level'. (org-dblock-write:clocktable): Add a :link parameter, linking headlines to their location in the Org agenda files. (org-get-tags-at): Bugfix: prevent `org-back-to-heading' from throwing an error when getting tags before headlines. (org-timestamp-change, org-modify-ts-extra) (org-ts-regexp1): Fix timestamp editing. (org-agenda-custom-commands-local-options): New constant. (org-agenda-custom-commands): Use `org-agenda-custom-commands-local-options' to improve customize type. "htmlize": Removed hack to fix face problem with htmlize, it no longer seem necessary. (org-follow-link-hook): New hook. (org-agenda-custom-commands): Add "Component" as a tag for each item in a command serie. (org-open-at-point): Run `org-follow-link-hook'. (org-agenda-schedule): Bugfix: don't display marker type when it is nil. (org-store-link): org-irc required. (org-set-regexps-and-options): Parse the new logging options. (org-todo): Handle the new ways of recording state change stuff. (org-columns-open-link): Fix bug with opening link in column view. (org-todo): Make sure that LOGGING properties are honoured. (org-todo-keywords): Improve docstring. (org-startup-options): Cleanup startup options. (org-set-regexps-and-options): Process the "!" markers. (org-todo): Respect the new logging stuff. (org-add-log-maybe): New parameter HOW that defines how logging should be done and also overrides PURPOSE. Add a docstring. (org-add-log-note): Check if we really need to ask for a note. (org-get-current-options): Digest the new keyword. (org-agenda-reset-markers): Rename from `org-agenda-maybe-reset-markers'. Remove FORCE argument. (org-diary, org-agenda-quit, org-prepare-agenda): Call the renamed function, without force argument. (org-buffer-property-keys): Bind local variables s and p. (org-make-tags-matcher): Allow "" to match an empty or non-existent property value. (org-export-as-html): Join unsorted lists when they directly follow each other. Such lists may be created by headlines that are converted to lists. (org-export-as-html): Use :html-extension instead of org-export-html-extension. (org-store-link): Support for links from `rmail-summary-mode'. (org-columns-new, org-complete, org-set-property): Set the `include-columns' argument in the call to `org-buffer-property-keys'. (org-buffer-property-keys): New argument `include-columns', to include properties expected by any of the COLUMS formats in the current buffer. (org-cleaned-string-for-export): Get rid of drawers first, so that they will be removed also in the text before the first headline. (org-clock-report): Show the clocktable when found. (org-refile): Fix positioning bug when `org-reverse-note-order' is nil. (org-version): With prefix argument, insert `org-version' at point. (org-agenda-goto): Recenter the window after finding the target location, to make sure the correct position will be displayed. (org-agenda-get-deadlines): Don't scale priority with the warning period. (org-insert-heading): Don't break line in the middle of the line. (org-agenda-get-deadlines): Allow `org-deadline-warning-days' to be 0. (org-update-checkbox-count): Revamp to deal with hierarchical beckboxes. This was a patch from Miguel A. Figueroa-Villanueva. (org-schedule, org-deadline): Use `org-remove-timestamp-with-keyword' to make sure all such time stamps are removed. (org-mode): Support for `align'. (org-agenda-get-deadlines): Make sure priorities increase as the due date approaches and is passed. (org-remember-apply-template): Fix problem with tags that contain "_" or "@". (org-make-link-regexps): Improve the regular expression for plain links. (org-agenda-get-closed): List each clocking entry. (org-set-tags): Only tabify before tags if indent-tabs-mode is t. (org-archive-all-done): Fix incorrect number of stars in regexp. (org-global-tags-completion-table): Add the value of org-tag-alist in each buffer, to make sure that also unused tags will be available for completion. (org-columns-edit-value) (org-columns-next-allowed-value): Only update if not in agenda. (org-dblock-write:clocktable): Call `org-clocktable-steps'. (org-archive-subtree): Add the outline tree context as a property. (org-closest-date): New optional argument `prefer'. (org-goto-map, org-get-location): Implement auto-isearch. (org-goto-local-search-forward-headings) (org-get-entries-from-diary): Require diary-lib. (org-agenda-sunrise-sunset): Require solar. (org-export-html-style): Doc fix. (org-get-legal-level): New obsolete alias. (org-get-valid-level): Rename from `org-get-legal-level'. (org-promote, org-demote, org-archive-subtree) (org-remember-handler, org-refile, org-put-clock-overlay): Use it. (org-info): Use info rather than Info-goto-node. (org-entry-properties): Let-bind `clocksum'. (org-unmodified, org-cycle-emulate-tab) (org-descriptive-links, org-link-file-path-type) (org-remember-use-refile-when-interactive) (org-agenda-skip-timestamp-if-done, org-agenda-scheduled-leaders) (org-export-ascii-bullets, org-agenda-deadline-faces) (turn-on-orgstruct++, orgtbl-to-texinfo, org-mhe-get-header) (org-batch-agenda, org-batch-agenda-csv, org-fix-agenda-info) (org-kill-note-or-show-branches): Fix typos in docstrings. (org-html-do-expand): `org-export-html-convert-special-strings' added to the list of conversion. (org-infile-export-plist, org-get-current-options): Add support for "-" in the #+OPTION line to let user switch on/off special strings conversion. (org-export-plist-vars): New :html-table-tag property. (org-export-as-html, org-format-org-table-html) (org-format-table-table-html) Use the :html-table-tag property instead of the `org-export-html-table-tag' global value. (org-additional-option-like-keywords): Add "TBLFM". (org-entry-properties): Include the CLOCKSUM special property. (org-columns-edit-value): Do not allow to edit the special CLOCKSUM property. (org-flag-drawer): Use the original value of `outline-regexp'. (org-remember-handler): Add invisible-ok flag to call to `org-end-of-subtree'. (org-agenda-highlight-todo): Respect `org-agenda-todo-keyword-format'. (org-infile-export-plist): No restriction while searching for options. (org-remember-handler): Remove comments at the end of the buffer. (org-table-sort-lines): Make sure sorting works on link descritions only, and ignores the link. (org-sort-entries-or-items): Make sure the end of the subtree is included. (org-refile-use-outline-path): New allowed values `file' and `full-file-path'. (org-get-refile-targets): Respect new values for `org-refile-use-outline-path'. (org-agenda-get-restriction-and-command): DEL goes back to initial list. (org-export-as-xoxo): Restore point when done. (org-open-file): Allow multiple %s in command. (org-get-refile-targets): Interpret the new maxlevel setting. (org-refile-targets): New option `:maxlevel'. (org-copy-subtree): Include empty lines before but not after subtree. (org-move-item-down, org-move-item-up): Include empty lines before but not after item. (org-remember-apply-template): Defaults, completions and history for template prompts. Also, interpret new `%!' escape. (org-context-choices): New constant. (org-bound-and-true-p): New macro. (org-agenda-restriction-lock): New face. (org-open-at-point): Remove obsolete way to do redirection in shell links. (org-imenu-and-speedbar): New customization group. (org-entry-properties): Return keyword-less time strings. (org-clock-in): Use `org-clock-heading-function'. (org-calendar-holiday): Try to use `calendar-check-holidays' instead of the obsolete `check-calendar-holidays'. (org-export-html-special-string-regexps): New constant. (org-latex-and-export-specials): New face. (org-link-escape-chars): Use characters instead of strings. (org-link-escape-chars-browser, org-link-escape) (org-link-unescape): Use characters instead of strings. (org-export-html-convert-sub-super, org-html-do-expand): Check for protected text. (org-emphasis-alist): Additional `verbatim' flag. (org-set-emph-re): Handle the verbatim flag and compute `org-verbatim-re'. (org-cleaned-string-for-export): Protect verbatim elements. (org-additional-option-like-keywords): Add new keywords. (org-get-entry): Rename from `org-get-cleaned-entry'. (org-agenda-get-scheduled, org-agenda-get-blocks): Use `org-agenda-skip-scheduled-if-done'. (org-prepare-agenda-buffers): Allow buffers as arguments. (org-entry-properties): Add CATEGORY as a special property. (org-use-property-inheritance): Allow a list of properties as a value. (org-eval-in-calendar): No longer update the prompt. (org-read-date-popup-calendar): Rename from `org-popup-calendar-for-date-prompt'. (org-remember-apply-template): Define `remember-finalize' if it is not yet defined. (org-read-date): Respect the setting of `org-read-date-prefer-future'. Use `org-read-date-analyze'. (org-set-font-lock-defaults): Use `org-archive-tag' instead of a hardcoded string. (org-remember-apply-template): Use `remember-finalize' instead of `remember-buffer'. (org-columns-compute, org-column-number-to-string) (org-columns-uncompile-format, org-columns-compile-format) (org-columns-compile-format): Handle printf format specifier. (org-columns-new, org-column-number-to-string) (org-columns-uncompile-format, org-columns-compile-format): Support for new currency summary type. (org-tree-to-indirect-buffer): Do not kill old buffer when `org-indirect-buffer-display' is `new-frame'. (org-indirect-buffer-display): Document that `new-frame' leads to indiret buffer proliferation. (org-agenda-list): Use `org-extend-today-until'. (org-format-org-table-html): Use lower-case for <col> tag. (org-agenda-execute): New command. (org-agenda-mode-map): Keybindings of "g" "G", "e" modified. (org-remember-apply-template): Use `org-select-remember-template'. (org-region-active-p): Use `use-region-p'. (org-cycle-hide-archived-subtrees) (org-table-rotate-recalc-marks, org-mark-ring-push) (org-follow-info-link, org-mhe-get-message-folder-from-index) (org-auto-repeat-maybe, org-store-log-note, org-delete-property) (org-evaluate-time-range, org-edit-agenda-file-list): (org-paste-subtree): (org-calendar-holiday): Use calendar-check-holidays instead of the obsolete check-calendar-holidays. (org-table-formula-substitute-names): Remove forgotten temporary debugging code. (org-agenda-get-restriction-and-command): Use `mapc' instead of `mapcar'. (org-agenda-list): Numeric prefix argument can specify the number of days. (remember-register, remember-buffer): Prevent byte compiler from complaining. (org-todo): Save and restore match data. (org-no-warnings): New macro. (org-columns-eval): Use `org-no-warnings'. (org-version): Change to 5.13e. (org-agenda-file-regexp): Fix typo in docstring. (org-add-planning-info): Fix bug in parenthesis settings. (org-scan-tags): Catch the case of indirect buffers with no filename. (org-fast-tag-selection, org-export-as-ascii, org-export-as-html): Re-install switch to mapc, had been removed by accident. (org-columns-map): New binding `C-c C-o'. (org-columns-menu): Change menu text and added new entry. (org-columns-eval): Document the use of `next-line'. (org-columns-follow-link): Remove function. (org-read-date-get-relative): Fix typo in docstring. (org-read-date-get-relative): Leading +/- is not optional. (org-agenda-get-restriction-and-command): Always resize window on first loop cycle. (org-agenda-open-link): Make sure the link abbreviations are present in the agenda buffer. (org-agenda-files): Allow directories in the variable. (org-agenda): Use `org-agenda-get-restriction-and-command'. (org-todo-blocker-hook, org-todo-trigger-hook): New hooks. (org-remember-apply-template): Catch C-g and make sure window configuration is restored. (org-agenda-open-link): Make it work with several links in the line. (org-drawers, org-set-regexps-and-options) (org-get-current-options): Add support for a DRAWERS in-buffer option. (org-fit-agenda-window): Use `org-agenda-window-frame-fractions'. (org-agenda-to-appt): Require calendar. (org-entry-get-with-inheritance): Widen for search. (org-columns-display-here): Don't mark buffer as modified when adding space characters to accomodate column overlays. (org-export-as-html): Better formatting of tags in the toc. (org-columns-display-here): Make the ITEM column as compact as possible. (org-remember-templates): Customization interface improved. (org-export-with-property-drawer): Variable removed. (org-sort-entries): Rewrite using `sort-subr'. (org-set-property): More appropriate completion during interactive use. (org-sort-entries): Allow sorting by property. (org-additional-option-like-keywords): Add more values. (org-sort-entries-or-items): Rename from `org-sort-entries'. (org-at-timestamp-p, org-timestamp-change) (org-remember-templates): First element of each entry is now a name for the template. (org-store-log-note): Check for `org-note-abort'. (org-kill-note-or-show-branches): New command. (org-cut-subtree, org-copy-subtree): New argument N to act on N sequential subtrees. (org-paste-subtree): Fix the level at which a tree is pasted. (org-fit-agenda-window): Limitations on window size removed. (org-agenda-find-same-or-today-or-agenda): Rename from `org-agenda-find-today-or-agenda'. (org-agenda-scheduled-leaders) (org-agenda-get-deadlines): Use `org-agenda-deadline-leaders'. (org-agenda-get-scheduled): Use `org-agenda-scheduled-leaders'. (org-export-with-tags, org-export-plist-vars) (org-infile-export-plist): New "tags" option. (org-cached-entry-get): Use `org-use-property-inheritance'. (org-remember-apply-template): Fix typo. (org-additional-option-like-keywords): New constant. (org-complete): Use `org-additional-option-like-keywords'. (org-clock-report): Only update the table at point, or insert a new one. (org-open-file): Use `start-process-shell-command' instead of `shell-command' with an ampersand. (org-deadline, org-schedule): New argument REMOVE to remove the date from the entry. (org-agenda-schedule, org-agenda-deadline): Pass the prefix argument to `org-schedule' and `org-deadline'. (org-trim): Use the correct expressions for beginning and end of the string. (org-get-cleaned-entry): Trim the string before returning it. (org-agenda-tags-column): Rename from `org-agenda-align-tags-to-column'. (org-agenda-align-tags): Allow negative values for `org-agenda-tags-column'. (org-insert-labeled-timestamps-before-properties-drawer): Remove var. (org-agenda-to-appt): New optional argument FILTER. (org-complete): Use `org-completion-fallback-command'. (org-find-base-buffer-visiting): Catch the case that there is no buffer visiting the file. (org-todo): Use `org-property-or-variable-value' (org-prepare-agenda, org-agenda-list): Use `org-agenda-compact-blocks'. (org-agenda-schedule, org-agenda-deadline): Call `org-agenda-show-new-time'. (org-agenda-show-new-time): New argument PREFIX. (org-colgroup-info-to-vline-list): Fix but that cause a shift in the vertical lines. (org-buffer-property-keys): New argument INCLUDE-DEFAULTS. (org-maybe-renumber-ordered-list, org-cycle-list-bullet) (org-indent-item): No arg in call to `org-fix-bullet-type'. (org-fix-bullet-type): Remove argument. (org-read-date): Check for am/pm twice, to catch the end time. (org-goto-map): Use `suppress-keymap'. (org-remember-apply-template): Respect the dynamically scoped selection character. (org-kill-is-subtree-p): Use `org-outline-regexp'. (org-outline-regexp): New constant. (org-remember-handler): Throw error when the target file is not in org-mode. (org-cleaned-string-for-export): No longer call `org-export-latex-cleaned-string' with an argument. (org-get-tags): Returns now a list, not a string. (org-archive-subtree): No need to split return of `org-get-tags'. (org-set-tags, org-entry-properties): Call `org-get-tags-string' instead of `org-get-tags'. (org-agenda-format-date): Rename from `org-agenda-date-format'. (org-time-from-absolute, org-agenda-format-date-aligned): New funs. (org-compatible-face): New argument INHERITS. Inherit from this face if possible. (org-level-1, org-level-2, org-level-3, org-level-4) (org-level-5, org-level-6, org-level-7, org-level-8) (org-special-keyword, org-drawer, org-column, org-warning) (org-archived, org-todo, org-done, org-headline-done, org-table) (org-formula, org-code, org-agenda-structure) (org-scheduled-today, org-scheduled-previously) (org-upcoming-deadline, org-time-grid): Call `org-compatible-face' in the new way. (org-get-heading): New argument NO-TAGS. (org-fast-tag-selection-include-todo): Made defvar instead of defcustom, feature is not deprecated. (org-remember-store-without-prompt): New default value t. (org-set-regexps-and-options): #+TODO is an alias for SEQ_TODO. Compute the log states. (org-goto-map): More commands copied from global map. Also bind `org-occur'. (org-goto): Made into a general lookup command. (org-get-location): Complete rewrite. (org-goto-ret, org-goto-left, org-goto-right, org-goto-quit): Set the new variables. (org-paste-subtree): Whitespace insertion strategy revised. (org-remember-apply-template): Protect v-A from the possibility that v-a might be nil. (org-remember-handler): Insertion rules revised. (org-todo): Respect org-todo-log-states. (org-entry-get-with-inheritance): Use `org-up-heading-safe'. (org-confirm-shell-link-function) (org-confirm-elisp-link-function): Doc fixes. (org-re): Also replace the :alpha: class. (org-todo-tag-alist): Variable removed. (org-log-done): Docstring fixed. (org-deadline-warning-days): New default value 14. (org-tag-alist): Docstring fixed. (org-export-language-setup): New languages added. (org-set-regexps-and-options): Compute the new variables. (org-paste-subtree): Cleaning up. (org-remember-apply-template): New escape %A. (org-todo): Call fast TODO selection. (org-add-log-note): Allow prefix for abort exit. (org-at-property-p, org-entry-properties) (org-columns-get-autowidth-alist): Use :alpha: class. (org-agenda-get-deadlines): Use `org-get-wdays'. (org-agenda-get-deadlines): Reverse ee before returning. (org-format-agenda-item): New argument REMOVE-RE. (org-agenda-convert-date): Baha'i calendar added. (org-infile-export-plist): Also find DATE line. (org-export-as-html, org-export-as-ascii): Use the date format. (org-shiftup, org-shiftdown): Use. `org-edit-timestamp-down-means-later'. (org-columns-compile-format) (org-columns-get-autowidth-alist, org-buffer-property-keys) (org-entry-properties, org-at-property-p): Allow [:alnum:] in property names. (org-export-visible): Fix drawers before export. (org-do-sort): Allow sorting by priority. (org-agenda-files): Ignore non-existing files. (org-ellipsis): All a face as value. (org-mode): Interprete the face value of `org-ellipsis'. (org-archive-subtree): Store context info in archived entry. (org-fast-tag-selection): Allow setting TODO states through this interface. (org-cycle): Docstring updated. (org-set-font-lock-defaults, org-agenda-highlight-todo): Use `org-get-todo-face'. (org-table-edit-field, org-table-show-reference) (org-table-edit-formulas, org-add-log-note) (org-fast-tag-selection, org-agenda, org-prepare-agenda) (org-timeline): Use `org-switch-to-buffer-other-window' instead of `switch-to-buffer-other-window' to make sure that the temporary windows show up on the current frame. (org-mhe-get-message-real-folder, org-batch-store-agenda-views) (org-get-entries-from-diary, org-replace-region-by-html): Don't allow pop-up frames. (org-agenda-get-deadlines, org-agenda-get-scheduled): Fix problems with time-of-day. (org-agenda-get-scheduled, org-agenda-get-deadlines): Fix problems with listing items that are DONE. (org-change-tag-in-region): New command. (org-agenda-skip-scheduled-if-done) (org-agenda-skip-deadline-if-done): Docstring clarified. (org-mode): Hide drawers on startup. (org-set-regexps-and-options): Use `org-remove-keyword-keys'. (org-agenda-skip): Allow a form for `org-agenda-skip-function'. (org-agenda-redo): Re-use local settings. (org-agenda): Store local settings. (org-agenda-get-deadlines, org-agenda-get-scheduled): Also handle entries on their due date. (org-agenda-get-timestamps): No longer handle the due dates of schedules and deadline items. (org-insert-link-global, org-open-at-point-global): New commands. (org-export-as-ascii): Call `org-cleaned-string-for-export' with a :for-ascii parameter. (org-skip-comments): Function removed. (org-cleaned-string-for-export): Handle special table lines. (org-entry-get-with-inheritance): Check global properties. (org-set-regexps-and-options): Find the #+PROPERTY line. (org-link-types): Change type into variable (was constant). (org-link-re-with-space, org-link-re-with-space2) (org-angle-link-re, org-plain-link-re, org-bracket-link-regexp) (org-bracket-link-analytic-regexp, org-any-link-re): Creation of these regular expressions happens now in the function `org-make-link-regexps'. (org-store-link): Call the functions in `org-store-link-functions'. (org-activate-tags): Force matches to be in headlines. (org-batch-store-agenda-views): Fix bug with killing agenda buffer. (org-columns-display-here): Make sure this works in a narrowed buffer by checking for point-min. (org-columns-display-here): Make the rest of the line intangible, so that point never can be there. (org-cleaned-string-for-export): Use `with-current-buffer'. (org-replace-region-by-html): Use `with-current-buffer'. (org-unfontify-region, org-do-occur, org-columns-display-here) (org-columns-remove-overlays, org-columns-quit) (org-columns-edit-value, org-columns-next-allowed-value) (org-eval-in-calendar, org-agenda-undo, org-no-read-only) (org-finalize-agenda, org-remove-subtree-entries-from-agenda) (org-agenda-todo, org-agenda-change-all-lines) (org-agenda-align-tags, org-agenda-priority) (org-agenda-set-tags, org-agenda-toggle-archive-tag) (org-agenda-show-new-time, org-cleaned-string-for-export) (org-export-grab-title-from-buffer) (org-export-as-ascii, org-export-as-html): Use `inhibit-read-only' instead of `buffer-read-only'. (org-export-as-html): Set `coding-system-for-write'. (org-archive-subtree): Fixed bug with modifying TODO keyword. (org-beginning-of-line): Also treat C-a special in items. (org-table-convert-refs-to-rc): Fixed problem with column reference after "..". (org-columns-compute): Don't mark buffer modified because of text properties. (org-batch-store-agenda-views): Use the variable `default-directory', not the function. (org-clock-out-if-current): Respect `org-clock-out-when-done'. (org-html-entities): Added HTML entities for smileys. (org-end-of-line): Move to end of line if in headline without tags. (org-agenda-day-view, org-agenda-week-view): Remember span as default. (org-columns-edit-value): Rename from `org-column-edit'. (org-columns-display-here-title): Rename from `org-overlay-columns-title'. (org-columns-remove-overlays): Rename from org-remove-column-overlays. (org-columns-get-autowidth-alist): Rename from `org-get-columns-autowidth-alist'. (org-columns-display-here): Rename from `org-overlay-columns'. (org-columns-new-overlay): Rename from `org-new-column-overlay'. (org-columns-quit): Rename from `org-column-quit'. (org-columns-show-value): Rename from `org-column-show-value'. (org-columns-current-fmt): Rename from `org-current-columns-fmt'. (org-columns-overlays): Rename from `org-column-overlays'. (org-columns-map): Rename from `org-column-map'. (org-columns-current-maxwidths): Rename from `org-current-columns-maxwidths'. (org-columns-begin-marker, org-columns-current-fmt-compiled) (org-previous-header-line-format) (org-columns-inhibit-recalculation) (org-columns-default-format): Rename from `org-default-columns-format'. (org-property-re): New constant. (orgstruct-mode): New minor mode. (org-cycle-list-bullet): New command. (org-special-properties, org-property-start-re) (org-property-end-re): New constants. (org-with-point-at): New macro. (org-insert-property-drawer): New command. (org-column): New face. (org-column-menu): New menu. (org-columns, org-agenda-columns): New commands. (org-properties): New customize group. (org-priority): Realign tags after changing priority. (org-preserve-lc): New macro. (org-update-checkbox-count): Catch case when there is no headline. (org-agenda-quit): Remove any column overlays. (org-beginning-of-item-list): Fixed bug when non-item line is indented too deep. (org-make-tags-matcher): Handle property matches. (org-table-recalculate): Swap evaluation order: Field formula first, then column formulas, but don't allow them to overwrite the field formulas. (org-table-eval-formula): New argument untouchable. (org-export-as-html): Fix replacement bug for XEmacs.
author Carsten Dominik <dominik@science.uva.nl>
date Tue, 08 Apr 2008 07:44:50 +0000
parents a1c6fb73c033
children 107ccd98fa12
line wrap: on
line source

/* Implements a lightweight menubar widget.
Copyright (C) 1992 Lucid, Inc.
Copyright (C) 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003, 2004,
  2005, 2006, 2007, 2008  Free Software Foundation, Inc.

This file is part of the Lucid Widget Library.

The Lucid Widget Library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

The Lucid Widget Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.  */

/* Created by devin@lucid.com */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "lisp.h"

#include <stdio.h>

#include <sys/types.h>
#if (defined __sun) && !(defined SUNOS41)
#define SUNOS41
#include <X11/Xos.h>
#undef SUNOS41
#else
#include <X11/Xos.h>
#endif
#include <X11/IntrinsicP.h>
#include <X11/ObjectP.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#include "xlwmenuP.h"

#ifdef emacs

/* Defined in xfns.c.  When config.h defines `static' as empty, we get
   redefinition errors when gray_bitmap is included more than once, so
   we're referring to the one include in xfns.c here.  */

extern int gray_bitmap_width;
extern int gray_bitmap_height;
extern char *gray_bitmap_bits;

#include "xterm.h"

#else /* not emacs */

#include <X11/bitmaps/gray>
#define gray_bitmap_width	gray_width
#define gray_bitmap_height	gray_height
#define gray_bitmap_bits	gray_bits

#endif /* not emacs */

static int pointer_grabbed;
static XEvent menu_post_event;

XFontStruct *xlwmenu_default_font;

static char
xlwMenuTranslations [] =
"<BtnDown>:	  start()\n\
<Motion>:	  drag()\n\
<BtnUp>:	  select()\n\
<Key>Shift_L:     nothing()\n\
<Key>Shift_R:     nothing()\n\
<Key>Meta_L:      nothing()\n\
<Key>Meta_R:      nothing()\n\
<Key>Control_L:   nothing()\n\
<Key>Control_R:   nothing()\n\
<Key>Hyper_L:     nothing()\n\
<Key>Hyper_R:     nothing()\n\
<Key>Super_L:     nothing()\n\
<Key>Super_R:     nothing()\n\
<Key>Alt_L:       nothing()\n\
<Key>Alt_R:       nothing()\n\
<Key>Caps_Lock:   nothing()\n\
<Key>Shift_Lock:  nothing()\n\
<KeyUp>Shift_L:   nothing()\n\
<KeyUp>Shift_R:   nothing()\n\
<KeyUp>Meta_L:    nothing()\n\
<KeyUp>Meta_R:    nothing()\n\
<KeyUp>Control_L: nothing()\n\
<KeyUp>Control_R: nothing()\n\
<KeyUp>Hyper_L:   nothing()\n\
<KeyUp>Hyper_R:   nothing()\n\
<KeyUp>Super_L:   nothing()\n\
<KeyUp>Super_R:   nothing()\n\
<KeyUp>Alt_L:     nothing()\n\
<KeyUp>Alt_R:     nothing()\n\
<KeyUp>Caps_Lock: nothing()\n\
<KeyUp>Shift_Lock:nothing()\n\
<Key>Return:      select()\n\
<Key>Down:        down()\n\
<Key>Up:          up()\n\
<Key>Left:        left()\n\
<Key>Right:       right()\n\
<Key>:            key()\n\
<KeyUp>:          key()\n\
";

/* FIXME: Space should toggle toggleable menu item but not remove the menu
   so you can toggle the next one without entering the menu again.  */

/* FIXME: Should ESC close one level of menu structure or the complete menu?  */

/* FIXME: F10 should enter the menu, the first one in the menu-bar.  */

#define offset(field) XtOffset(XlwMenuWidget, field)
static XtResource
xlwMenuResources[] =
{
#ifdef HAVE_X_I18N
  {XtNfontSet,  XtCFontSet, XtRFontSet, sizeof(XFontSet),
     offset(menu.fontSet), XtRFontSet, NULL},
#endif
  {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
     offset(menu.font), XtRString, "XtDefaultFont"},
  {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
     offset(menu.foreground), XtRString, "XtDefaultForeground"},
  {XtNdisabledForeground, XtCDisabledForeground, XtRPixel, sizeof(Pixel),
   offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
  {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
     offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
  {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
     offset(menu.margin), XtRImmediate, (XtPointer)1},
  {XtNhorizontalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
     offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
  {XtNverticalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
     offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
  {XtNarrowSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
     offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},

  {XmNshadowThickness, XmCShadowThickness, XtRDimension,
     sizeof (Dimension), offset (menu.shadow_thickness),
     XtRImmediate, (XtPointer)1},
  {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
     offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
  {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
     offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
  {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
     offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
  {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
     offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},

  {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
     offset(menu.open), XtRCallback, (XtPointer)NULL},
  {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
     offset(menu.select), XtRCallback, (XtPointer)NULL},
  {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
     offset(menu.highlight), XtRCallback, (XtPointer)NULL},
  {XtNenterCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
     offset(menu.enter), XtRCallback, (XtPointer)NULL},
  {XtNleaveCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
     offset(menu.leave), XtRCallback, (XtPointer)NULL},
  {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
     offset(menu.contents), XtRImmediate, (XtPointer)NULL},
  {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
     offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
  {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
     offset(menu.horizontal), XtRImmediate, (XtPointer)True},
};
#undef offset

static Boolean XlwMenuSetValues();
static void XlwMenuRealize();
static void XlwMenuRedisplay();
static void XlwMenuResize();
static void XlwMenuInitialize();
static void XlwMenuRedisplay();
static void XlwMenuDestroy();
static void XlwMenuClassInitialize();
static void Start();
static void Drag();
static void Down();
static void Up();
static void Left();
static void Right();
static void Select();
static void Key();
static void Nothing();
static int separator_height __P ((enum menu_separator));
static void pop_up_menu __P ((XlwMenuWidget, XButtonPressedEvent *));


static XtActionsRec
xlwMenuActionsList [] =
{
  {"start",		Start},
  {"drag",		Drag},
  {"down",		Down},
  {"up",		Up},
  {"left",		Left},
  {"right",		Right},
  {"select",		Select},
  {"key",		Key},
  {"MenuGadgetEscape",  Key},   /* Compatibility with Lesstif/Motif.  */
  {"nothing",		Nothing},
};

#define SuperClass ((CoreWidgetClass)&coreClassRec)

XlwMenuClassRec xlwMenuClassRec =
{
  {  /* CoreClass fields initialization */
    (WidgetClass) SuperClass,		/* superclass		  */
    "XlwMenu",				/* class_name		  */
    sizeof(XlwMenuRec),			/* size			  */
    XlwMenuClassInitialize,		/* class_initialize	  */
    NULL,				/* class_part_initialize  */
    FALSE,				/* class_inited		  */
    XlwMenuInitialize,			/* initialize		  */
    NULL,				/* initialize_hook	  */
    XlwMenuRealize,			/* realize		  */
    xlwMenuActionsList,			/* actions		  */
    XtNumber(xlwMenuActionsList),	/* num_actions		  */
    xlwMenuResources,			/* resources		  */
    XtNumber(xlwMenuResources),		/* resource_count	  */
    NULLQUARK,				/* xrm_class		  */
    TRUE,				/* compress_motion	  */
    XtExposeCompressMaximal,		/* compress_exposure	  */
    TRUE,				/* compress_enterleave    */
    FALSE,				/* visible_interest	  */
    XlwMenuDestroy,			/* destroy		  */
    XlwMenuResize,			/* resize		  */
    XlwMenuRedisplay,			/* expose		  */
    XlwMenuSetValues,			/* set_values		  */
    NULL,				/* set_values_hook	  */
    XtInheritSetValuesAlmost,		/* set_values_almost	  */
    NULL,				/* get_values_hook	  */
    NULL,				/* accept_focus		  */
    XtVersion,				/* version		  */
    NULL,				/* callback_private	  */
    xlwMenuTranslations,		/* tm_table		  */
    XtInheritQueryGeometry,		/* query_geometry	  */
    XtInheritDisplayAccelerator,	/* display_accelerator	  */
    NULL				/* extension		  */
  },  /* XlwMenuClass fields initialization */
  {
    0					/* dummy */
  },
};

WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;

int submenu_destroyed;

/* For debug, if installation-directory is non-nil this is not an installed
   Emacs.   In that case we do not grab the keyboard to make it easier to
   debug. */
#define GRAB_KEYBOARD  (EQ (Vinstallation_directory, Qnil))

static int next_release_must_exit;

/* Utilities */

/* Ungrab pointer and keyboard */
static void
ungrab_all (w, ungrabtime)
     Widget w;
     Time ungrabtime;
{
  XtUngrabPointer (w, ungrabtime);
  if (GRAB_KEYBOARD) XtUngrabKeyboard (w, ungrabtime);
}

/* Like abort, but remove grabs from widget W before.  */

static void
abort_gracefully (w)
     Widget w;
{
  if (XtIsShell (XtParent (w)))
    XtRemoveGrab (w);
  ungrab_all (w, CurrentTime);
  abort ();
}

static void
push_new_stack (mw, val)
     XlwMenuWidget mw;
     widget_value* val;
{
  if (!mw->menu.new_stack)
    {
      mw->menu.new_stack_length = 10;
      mw->menu.new_stack =
	(widget_value**)XtCalloc (mw->menu.new_stack_length,
				  sizeof (widget_value*));
    }
  else if (mw->menu.new_depth == mw->menu.new_stack_length)
    {
      mw->menu.new_stack_length *= 2;
      mw->menu.new_stack =
	(widget_value**)XtRealloc ((char*)mw->menu.new_stack,
				   mw->menu.new_stack_length * sizeof (widget_value*));
    }
  mw->menu.new_stack [mw->menu.new_depth++] = val;
}

static void
pop_new_stack_if_no_contents (mw)
     XlwMenuWidget mw;
{
  if (mw->menu.new_depth > 1)
    {
      if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
	mw->menu.new_depth -= 1;
    }
}

static void
make_old_stack_space (mw, n)
     XlwMenuWidget mw;
     int n;
{
  if (!mw->menu.old_stack)
    {
      mw->menu.old_stack_length = 10;
      mw->menu.old_stack =
	(widget_value**)XtCalloc (mw->menu.old_stack_length,
				  sizeof (widget_value*));
    }
  else if (mw->menu.old_stack_length < n)
    {
      mw->menu.old_stack_length *= 2;
      mw->menu.old_stack =
	(widget_value**)XtRealloc ((char*)mw->menu.old_stack,
				   mw->menu.old_stack_length * sizeof (widget_value*));
    }
}

/* Size code */
static int
string_width (mw, s)
     XlwMenuWidget mw;
     char *s;
{
  XCharStruct xcs;
  int drop;
#ifdef HAVE_X_I18N
  XRectangle ink, logical;
  if (mw->menu.fontSet)
    {
      XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
      return logical.width;
    }
#endif

  XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
  return xcs.width;

}

#ifdef HAVE_X_I18N
#define MENU_FONT_HEIGHT(mw) \
  ((mw)->menu.fontSet != NULL \
   ? (mw)->menu.font_extents->max_logical_extent.height   \
   : (mw)->menu.font->ascent + (mw)->menu.font->descent)
#define MENU_FONT_ASCENT(mw) \
  ((mw)->menu.fontSet != NULL \
   ? - (mw)->menu.font_extents->max_logical_extent.y \
   : (mw)->menu.font->ascent)
#else
#define MENU_FONT_HEIGHT(mw) \
  ((mw)->menu.font->ascent + (mw)->menu.font->descent)
#define MENU_FONT_ASCENT(mw) ((mw)->menu.font->ascent)
#endif

static int
arrow_width (mw)
     XlwMenuWidget mw;
{
  return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
}

/* Return the width of toggle buttons of widget MW.  */

static int
toggle_button_width (mw)
     XlwMenuWidget mw;
{
  return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
}


/* Return the width of radio buttons of widget MW.  */

static int
radio_button_width (mw)
     XlwMenuWidget mw;
{
  return toggle_button_width (mw) * 1.41;
}


static XtResource
nameResource[] =
{
  {"labelString",  "LabelString", XtRString, sizeof(String),
     0, XtRImmediate, 0},
};

static char*
resource_widget_value (mw, val)
     XlwMenuWidget mw;
     widget_value *val;
{
  if (!val->toolkit_data)
    {
      char* resourced_name = NULL;
      char* complete_name;
      XtGetSubresources ((Widget) mw,
			 (XtPointer) &resourced_name,
			 val->name, val->name,
			 nameResource, 1, NULL, 0);
      if (!resourced_name)
	resourced_name = val->name;
      if (!val->value)
	{
	  complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
	  strcpy (complete_name, resourced_name);
	}
      else
	{
	  int complete_length =
	    strlen (resourced_name) + strlen (val->value) + 2;
	  complete_name = XtMalloc (complete_length);
	  *complete_name = 0;
	  strcat (complete_name, resourced_name);
	  strcat (complete_name, " ");
	  strcat (complete_name, val->value);
	}

      val->toolkit_data = complete_name;
      val->free_toolkit_data = True;
    }
  return (char*)val->toolkit_data;
}

/* Returns the sizes of an item */
static void
size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
		height)
     XlwMenuWidget mw;
     widget_value* val;
     int horizontal_p;
     int* label_width;
     int* rest_width;
     int* button_width;
     int* height;
{
  enum menu_separator separator;

  if (lw_separator_p (val->name, &separator, 0))
    {
      *height = separator_height (separator);
      *label_width = 1;
      *rest_width = 0;
      *button_width = 0;
    }
  else
    {
      *height = MENU_FONT_HEIGHT (mw)
	+ 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;

      *label_width =
	string_width (mw, resource_widget_value (mw, val))
	  + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;

      *rest_width =  mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
      if (!horizontal_p)
	{
	  if (val->contents)
	    /* Add width of the arrow displayed for submenus.  */
	    *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
	  else if (val->key)
	    /* Add width of key equivalent string.  */
	    *rest_width += (string_width (mw, val->key)
			    + mw->menu.arrow_spacing);

	  if (val->button_type == BUTTON_TYPE_TOGGLE)
	    *button_width = (toggle_button_width (mw)
			     + mw->menu.horizontal_spacing);
	  else if (val->button_type == BUTTON_TYPE_RADIO)
	    *button_width = (radio_button_width (mw)
			     + mw->menu.horizontal_spacing);
	}
    }
}

static void
size_menu (mw, level)
     XlwMenuWidget mw;
     int level;
{
  unsigned int  label_width = 0;
  int		rest_width = 0;
  int		button_width = 0;
  int		max_rest_width = 0;
  int		max_button_width = 0;
  unsigned int  height = 0;
  int		horizontal_p = mw->menu.horizontal && (level == 0);
  widget_value*	val;
  window_state*	ws;

  if (level >= mw->menu.old_depth)
    abort_gracefully ((Widget) mw);

  ws = &mw->menu.windows [level];
  ws->width = 0;
  ws->height = 0;
  ws->label_width = 0;
  ws->button_width = 0;

  for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
    {
      size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
		      &button_width, &height);
      if (horizontal_p)
	{
	  ws->width += label_width + rest_width;
	  if (height > ws->height)
	    ws->height = height;
	}
      else
	{
	  if (label_width > ws->label_width)
	    ws->label_width = label_width;
	  if (rest_width > max_rest_width)
	    max_rest_width = rest_width;
	  if (button_width > max_button_width)
	    max_button_width = button_width;
	  ws->height += height;
	}
    }

  if (horizontal_p)
    ws->label_width = ws->button_width = 0;
  else
    {
      ws->width = ws->label_width + max_rest_width + max_button_width;
      ws->button_width = max_button_width;
    }

  ws->width += 2 * mw->menu.shadow_thickness;
  ws->height += 2 * mw->menu.shadow_thickness;

  if (horizontal_p)
    {
      ws->width += 2 * mw->menu.margin;
      ws->height += 2 * mw->menu.margin;
    }
}


/* Display code */

static void
draw_arrow (mw, window, gc, x, y, width, down_p)
     XlwMenuWidget mw;
     Window window;
     GC gc;
     int x;
     int y;
     int width;
     int down_p;
{
  Display *dpy = XtDisplay (mw);
  GC top_gc = mw->menu.shadow_top_gc;
  GC bottom_gc = mw->menu.shadow_bottom_gc;
  int thickness = mw->menu.shadow_thickness;
  int height = width;
  XPoint pt[10];
  /* alpha = atan (0.5)
     factor = (1 + sin (alpha)) / cos (alpha) */
  double factor = 1.62;
  int thickness2 = thickness * factor;

  y += (MENU_FONT_HEIGHT (mw) - height) / 2;

  if (down_p)
    {
      GC temp;
      temp = top_gc;
      top_gc = bottom_gc;
      bottom_gc = temp;
    }

  pt[0].x = x;
  pt[0].y = y + height;
  pt[1].x = x + thickness;
  pt[1].y = y + height - thickness2;
  pt[2].x = x + thickness2;
  pt[2].y = y + thickness2;
  pt[3].x = x;
  pt[3].y = y;
  XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);

  pt[0].x = x;
  pt[0].y = y;
  pt[1].x = x + thickness;
  pt[1].y = y + thickness2;
  pt[2].x = x + width - thickness2;
  pt[2].y = y + height / 2;
  pt[3].x = x + width;
  pt[3].y = y + height / 2;
  XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);

  pt[0].x = x;
  pt[0].y = y + height;
  pt[1].x = x + thickness;
  pt[1].y = y + height - thickness2;
  pt[2].x = x + width - thickness2;
  pt[2].y = y + height / 2;
  pt[3].x = x + width;
  pt[3].y = y + height / 2;
  XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
}



static void
draw_shadow_rectangle (mw, window, x, y, width, height, erase_p, down_p)
     XlwMenuWidget mw;
     Window window;
     int x;
     int y;
     int width;
     int height;
     int erase_p;
     int down_p;
{
  Display *dpy = XtDisplay (mw);
  GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
  GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
  int thickness = mw->menu.shadow_thickness;
  XPoint points [4];

  if (!erase_p && down_p)
    {
      GC temp;
      temp = top_gc;
      top_gc = bottom_gc;
      bottom_gc = temp;
    }

  points [0].x = x;
  points [0].y = y;
  points [1].x = x + width;
  points [1].y = y;
  points [2].x = x + width - thickness;
  points [2].y = y + thickness;
  points [3].x = x;
  points [3].y = y + thickness;
  XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x;
  points [0].y = y + thickness;
  points [1].x = x;
  points [1].y = y + height;
  points [2].x = x + thickness;
  points [2].y = y + height - thickness;
  points [3].x = x + thickness;
  points [3].y = y + thickness;
  XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x + width;
  points [0].y = y;
  points [1].x = x + width - thickness;
  points [1].y = y + thickness;
  points [2].x = x + width - thickness;
  points [2].y = y + height - thickness;
  points [3].x = x + width;
  points [3].y = y + height - thickness;
  XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x;
  points [0].y = y + height;
  points [1].x = x + width;
  points [1].y = y + height;
  points [2].x = x + width;
  points [2].y = y + height - thickness;
  points [3].x = x + thickness;
  points [3].y = y + height - thickness;
  XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
}


static void
draw_shadow_rhombus (mw, window, x, y, width, height, erase_p, down_p)
     XlwMenuWidget mw;
     Window window;
     int x;
     int y;
     int width;
     int height;
     int erase_p;
     int down_p;
{
  Display *dpy = XtDisplay (mw);
  GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
  GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
  int thickness = mw->menu.shadow_thickness;
  XPoint points [4];

  if (!erase_p && down_p)
    {
      GC temp;
      temp = top_gc;
      top_gc = bottom_gc;
      bottom_gc = temp;
    }

  points [0].x = x;
  points [0].y = y + height / 2;
  points [1].x = x + thickness;
  points [1].y = y + height / 2;
  points [2].x = x + width / 2;
  points [2].y = y + thickness;
  points [3].x = x + width / 2;
  points [3].y = y;
  XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x + width / 2;
  points [0].y = y;
  points [1].x = x + width / 2;
  points [1].y = y + thickness;
  points [2].x = x + width - thickness;
  points [2].y = y + height / 2;
  points [3].x = x + width;
  points [3].y = y + height / 2;
  XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x;
  points [0].y = y + height / 2;
  points [1].x = x + thickness;
  points [1].y = y + height / 2;
  points [2].x = x + width / 2;
  points [2].y = y + height - thickness;
  points [3].x = x + width / 2;
  points [3].y = y + height;
  XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x + width / 2;
  points [0].y = y + height;
  points [1].x = x + width / 2;
  points [1].y = y + height - thickness;
  points [2].x = x + width - thickness;
  points [2].y = y + height / 2;
  points [3].x = x + width;
  points [3].y = y + height / 2;
  XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
}


/* Draw a toggle button on widget MW, X window WINDOW.  X/Y is the
   top-left corner of the menu item.  SELECTED_P non-zero means the
   toggle button is selected.  */

static void
draw_toggle (mw, window, x, y, selected_p)
     XlwMenuWidget mw;
     Window window;
     int x, y, selected_p;
{
  int width, height;

  width = toggle_button_width (mw);
  height = width;
  x += mw->menu.horizontal_spacing;
  y += (MENU_FONT_ASCENT (mw) - height) / 2;
  draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
}


/* Draw a radio button on widget MW, X window WINDOW.  X/Y is the
   top-left corner of the menu item.  SELECTED_P non-zero means the
   toggle button is selected.  */

static void
draw_radio (mw, window, x, y, selected_p)
     XlwMenuWidget mw;
     Window window;
     int x, y, selected_p;
{
  int width, height;

  width = radio_button_width (mw);
  height = width;
  x += mw->menu.horizontal_spacing;
  y += (MENU_FONT_ASCENT (mw) - height) / 2;
  draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
}


/* Draw a menu separator on widget MW, X window WINDOW.  X/Y is the
   top-left corner of the menu item.  WIDTH is the width of the
   separator to draw.  TYPE is the separator type.  */

static void
draw_separator (mw, window, x, y, width, type)
     XlwMenuWidget mw;
     Window window;
     int x, y, width;
     enum menu_separator type;
{
  Display *dpy = XtDisplay (mw);
  XGCValues xgcv;

  switch (type)
    {
    case SEPARATOR_NO_LINE:
      break;

    case SEPARATOR_SINGLE_LINE:
      XDrawLine (dpy, window, mw->menu.foreground_gc,
		 x, y, x + width, y);
      break;

    case SEPARATOR_DOUBLE_LINE:
      draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
      draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
      break;

    case SEPARATOR_SINGLE_DASHED_LINE:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
      XDrawLine (dpy, window, mw->menu.foreground_gc,
		 x, y, x + width, y);
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
      break;

    case SEPARATOR_DOUBLE_DASHED_LINE:
      draw_separator (mw, window, x, y, width,
		      SEPARATOR_SINGLE_DASHED_LINE);
      draw_separator (mw, window, x, y + 2, width,
		      SEPARATOR_SINGLE_DASHED_LINE);
      break;

    case SEPARATOR_SHADOW_ETCHED_IN:
      XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
		 x, y, x + width, y);
      XDrawLine (dpy, window, mw->menu.shadow_top_gc,
		 x, y + 1, x + width, y + 1);
      break;

    case SEPARATOR_SHADOW_ETCHED_OUT:
      XDrawLine (dpy, window, mw->menu.shadow_top_gc,
		 x, y, x + width, y);
      XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
		 x, y + 1, x + width, y + 1);
      break;

    case SEPARATOR_SHADOW_ETCHED_IN_DASH:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      break;

    case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      break;

    case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
      draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
      draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
      break;

    case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
      draw_separator (mw, window, x, y, width,
		      SEPARATOR_SHADOW_ETCHED_OUT);
      draw_separator (mw, window, x, y + 3, width,
		      SEPARATOR_SHADOW_ETCHED_OUT);
      break;

    case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      draw_separator (mw, window, x, y, width,
		      SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      break;

    case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      draw_separator (mw, window, x, y, width,
		      SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      break;

    default:
      abort ();
    }
}


/* Return the pixel height of menu separator SEPARATOR.  */

static int
separator_height (separator)
     enum menu_separator separator;
{
  switch (separator)
    {
    case SEPARATOR_NO_LINE:
      return 2;

    case SEPARATOR_SINGLE_LINE:
    case SEPARATOR_SINGLE_DASHED_LINE:
      return 1;

    case SEPARATOR_DOUBLE_LINE:
    case SEPARATOR_DOUBLE_DASHED_LINE:
      return 3;

    case SEPARATOR_SHADOW_ETCHED_IN:
    case SEPARATOR_SHADOW_ETCHED_OUT:
    case SEPARATOR_SHADOW_ETCHED_IN_DASH:
    case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
      return 2;

    case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
      return 5;

    default:
      abort ();
    }
}


/* Display the menu item and increment where.x and where.y to show how large
   the menu item was.  */

static void
display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
		   just_compute_p)
     XlwMenuWidget mw;
     widget_value* val;
     window_state* ws;
     XPoint* where;
     Boolean highlighted_p;
     Boolean horizontal_p;
     Boolean just_compute_p;
{
  GC deco_gc;
  GC text_gc;
  int font_height = MENU_FONT_HEIGHT (mw);
  int font_ascent = MENU_FONT_ASCENT (mw);
  int shadow = mw->menu.shadow_thickness;
  int margin = mw->menu.margin;
  int h_spacing = mw->menu.horizontal_spacing;
  int v_spacing = mw->menu.vertical_spacing;
  int label_width;
  int rest_width;
  int button_width;
  int height;
  int width;
  enum menu_separator separator;
  int separator_p = lw_separator_p (val->name, &separator, 0);

  /* compute the sizes of the item */
  size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
		  &button_width, &height);

  if (horizontal_p)
    width = label_width + rest_width;
  else
    {
      label_width = ws->label_width;
      width = ws->width - 2 * shadow;
    }

  /* Only highlight an enabled item that has a callback. */
  if (highlighted_p)
    if (!val->enabled || !(val->call_data || val->contents))
      highlighted_p = 0;

  /* do the drawing. */
  if (!just_compute_p)
    {
      /* Add the shadow border of the containing menu */
      int x = where->x + shadow;
      int y = where->y + shadow;

      if (horizontal_p)
	{
	  x += margin;
	  y += margin;
	}

      /* pick the foreground and background GC. */
      if (val->enabled)
	text_gc = mw->menu.foreground_gc;
      else
	text_gc = mw->menu.disabled_gc;
      deco_gc = mw->menu.foreground_gc;

      if (separator_p)
	{
	  draw_separator (mw, ws->window, x, y, width, separator);
	}
      else
	{
	  int x_offset = x + h_spacing + shadow;
	  char* display_string = resource_widget_value (mw, val);
	  draw_shadow_rectangle (mw, ws->window, x, y, width, height, True,
				 False);

	  /* Deal with centering a menu title. */
	  if (!horizontal_p && !val->contents && !val->call_data)
	    {
	      int l = string_width (mw, display_string);

	      if (width > l)
		x_offset = (width - l) >> 1;
	    }
	  else if (!horizontal_p && ws->button_width)
	    x_offset += ws->button_width;


#ifdef HAVE_X_I18N
          if (mw->menu.fontSet)
            XmbDrawString (XtDisplay (mw), ws->window, mw->menu.fontSet,
                           text_gc, x_offset,
                           y + v_spacing + shadow + font_ascent,
                           display_string, strlen (display_string));
          else
#endif
          XDrawString (XtDisplay (mw), ws->window,
		       text_gc, x_offset,
		       y + v_spacing + shadow + font_ascent,
		       display_string, strlen (display_string));

	  if (!horizontal_p)
	    {
	      if (val->button_type == BUTTON_TYPE_TOGGLE)
		draw_toggle (mw, ws->window, x, y + v_spacing + shadow,
			     val->selected);
	      else if (val->button_type == BUTTON_TYPE_RADIO)
		draw_radio (mw, ws->window, x, y + v_spacing + shadow,
			    val->selected);

	      if (val->contents)
		{
		  int a_w = arrow_width (mw);
		  draw_arrow (mw, ws->window, deco_gc,
			      x + width - a_w
			      - mw->menu.horizontal_spacing
			      - mw->menu.shadow_thickness,
			      y + v_spacing + shadow, a_w,
			      highlighted_p);
		}
	      else if (val->key)
		{
#ifdef HAVE_X_I18N
                  if (mw->menu.fontSet)
                    XmbDrawString (XtDisplay (mw), ws->window,
                                   mw->menu.fontSet,
                                   text_gc,
                                   x + label_width + mw->menu.arrow_spacing,
                                   y + v_spacing + shadow + font_ascent,
                                   val->key, strlen (val->key));
                  else
#endif
		  XDrawString (XtDisplay (mw), ws->window,
			       text_gc,
			       x + label_width + mw->menu.arrow_spacing,
			       y + v_spacing + shadow + font_ascent,
			       val->key, strlen (val->key));
		}
	    }
	  else
	    {
	      XDrawRectangle (XtDisplay (mw), ws->window,
			      mw->menu.background_gc,
			      x + shadow, y + shadow,
			      label_width + h_spacing - 1,
			      font_height + 2 * v_spacing - 1);
	      draw_shadow_rectangle (mw, ws->window, x, y, width, height,
				     True, False);
	    }

	  if (highlighted_p)
	    draw_shadow_rectangle (mw, ws->window, x, y, width, height, False,
				   False);
	}
    }

  where->x += width;
  where->y += height;
}

static void
display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
	      this, that)
     XlwMenuWidget mw;
     int level;
     Boolean just_compute_p;
     XPoint* highlighted_pos;
     XPoint* hit;
     widget_value** hit_return;
     widget_value* this;
     widget_value* that;
{
  widget_value*	val;
  widget_value* following_item;
  window_state* ws;
  XPoint	where;
  int horizontal_p = mw->menu.horizontal && (level == 0);
  int highlighted_p;
  int just_compute_this_one_p;
  /* This is set nonzero if the element containing HIGHLIGHTED_POS
     is disabled, so that we do not return any subsequent element either.  */
  int no_return = 0;
  enum menu_separator separator;

  if (level >= mw->menu.old_depth)
    abort_gracefully ((Widget) mw);

  if (level < mw->menu.old_depth - 1)
    following_item = mw->menu.old_stack [level + 1];
  else
    following_item = NULL;

  if (hit)
    *hit_return = NULL;

  where.x = 0;
  where.y = 0;

  ws = &mw->menu.windows [level];
  for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
    {
      highlighted_p = val == following_item;
      if (highlighted_p && highlighted_pos)
	{
	  if (horizontal_p)
	    highlighted_pos->x = where.x;
	  else
	    highlighted_pos->y = where.y;
	}

      just_compute_this_one_p =
	just_compute_p || ((this || that) && val != this &&  val != that);

      display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
			 just_compute_this_one_p);

      if (highlighted_p && highlighted_pos)
	{
	  if (horizontal_p)
	    highlighted_pos->y = where.y;
	  else
	    highlighted_pos->x = where.x;
	}

      if (hit
	  && !*hit_return
	  && (horizontal_p ? hit->x < where.x : hit->y < where.y)
	  && !lw_separator_p (val->name, &separator, 0)
	  && !no_return)
	{
	  if (val->enabled)
	    *hit_return = val;
	  else 
            no_return = 1;
          if (mw->menu.inside_entry != val) 
            {
              if (mw->menu.inside_entry)
                XtCallCallbackList ((Widget)mw, mw->menu.leave,
                                    (XtPointer) mw->menu.inside_entry);
              mw->menu.inside_entry = val;
              XtCallCallbackList ((Widget)mw, mw->menu.enter,
                                  (XtPointer) mw->menu.inside_entry);
            }
	}

      if (horizontal_p)
	where.y = 0;
      else
	where.x = 0;
    }

  if (!just_compute_p)
    draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height,
			   False, False);
}

/* Motion code */
static void
set_new_state (mw, val, level)
     XlwMenuWidget mw;
     widget_value* val;
     int level;
{
  int i;

  mw->menu.new_depth = 0;
  for (i = 0; i < level; i++)
    push_new_stack (mw, mw->menu.old_stack [i]);
  push_new_stack (mw, val);
}

static void
make_windows_if_needed (mw, n)
     XlwMenuWidget mw;
     int n;
{
  int i;
  int start_at;
  XSetWindowAttributes xswa;
  int mask;
  Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  window_state* windows;

  if (mw->menu.windows_length >= n)
    return;

  xswa.save_under = True;
  xswa.override_redirect = True;
  xswa.background_pixel = mw->core.background_pixel;
  xswa.border_pixel = mw->core.border_pixel;
  xswa.event_mask =
    ExposureMask | PointerMotionMask | PointerMotionHintMask
      | ButtonReleaseMask | ButtonPressMask;
  xswa.cursor = mw->menu.cursor_shape;
  mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
    | CWEventMask | CWCursor;

  if (!mw->menu.windows)
    {
      mw->menu.windows =
	(window_state*)XtMalloc (n * sizeof (window_state));
      start_at = 0;
    }
  else
    {
      mw->menu.windows =
	(window_state*)XtRealloc ((char*)mw->menu.windows,
				  n * sizeof (window_state));
      start_at = mw->menu.windows_length;
    }
  mw->menu.windows_length = n;

  windows = mw->menu.windows;

  for (i = start_at; i < n; i++)
   {
     windows [i].x = 0;
     windows [i].y = 0;
     windows [i].width = 1;
     windows [i].height = 1;
     windows [i].window =
       XCreateWindow (XtDisplay (mw), root, 0, 0, 1, 1,
		      0, 0, CopyFromParent, CopyFromParent, mask, &xswa);
  }
}

/* Value is non-zero if WINDOW is part of menu bar widget W.  */

int
xlwmenu_window_p (w, window)
     Widget w;
     Window window;
{
  XlwMenuWidget mw = (XlwMenuWidget) w;
  int i;

  for (i = 0; i < mw->menu.windows_length; ++i)
    if (window == mw->menu.windows[i].window)
      break;

  return i < mw->menu.windows_length;
}

/* Make the window fit in the screen */
static void
fit_to_screen (mw, ws, previous_ws, horizontal_p)
     XlwMenuWidget mw;
     window_state* ws;
     window_state* previous_ws;
     Boolean horizontal_p;
{
  unsigned int screen_width = WidthOfScreen (XtScreen (mw));
  unsigned int screen_height = HeightOfScreen (XtScreen (mw));
  /* 1 if we are unable to avoid an overlap between
     this menu and the parent menu in the X dimension.  */
  int horizontal_overlap = 0;

  if (ws->x < 0)
    ws->x = 0;
  else if (ws->x + ws->width > screen_width)
    {
      if (!horizontal_p)
	/* The addition of shadow-thickness for a sub-menu's position is
	   to reflect a similar adjustment when the menu is displayed to
	   the right of the invoking menu-item; it makes the sub-menu
	   look more `attached' to the menu-item.  */
	ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
      else
	ws->x = screen_width - ws->width;
      if (ws->x < 0)
	{
	  ws->x = 0;
	  horizontal_overlap = 1;
	}
    }
  /* If we overlap in X, try to avoid overlap in Y.  */
  if (horizontal_overlap
      && ws->y < previous_ws->y + previous_ws->height
      && previous_ws->y < ws->y + ws->height)
    {
      /* Put this menu right below or right above PREVIOUS_WS
	 if there's room.  */
      if (previous_ws->y + previous_ws->height + ws->height < screen_height)
	ws->y = previous_ws->y + previous_ws->height;
      else if (previous_ws->y - ws->height > 0)
	ws->y = previous_ws->y - ws->height;
    }

  if (ws->y < 0)
    ws->y = 0;
  else if (ws->y + ws->height > screen_height)
    {
      if (horizontal_p)
	ws->y = previous_ws->y - ws->height;
      else
	ws->y = screen_height - ws->height;
      if (ws->y < 0)
        ws->y = 0;
    }
}

/* Updates old_stack from new_stack and redisplays. */
static void
remap_menubar (mw)
     XlwMenuWidget mw;
{
  int i;
  int last_same;
  XPoint selection_position;
  int old_depth = mw->menu.old_depth;
  int new_depth = mw->menu.new_depth;
  widget_value** old_stack;
  widget_value** new_stack;
  window_state* windows;
  widget_value* old_selection;
  widget_value* new_selection;

  /* Check that enough windows and old_stack are ready. */
  make_windows_if_needed (mw, new_depth);
  make_old_stack_space (mw, new_depth);
  windows = mw->menu.windows;
  old_stack = mw->menu.old_stack;
  new_stack = mw->menu.new_stack;

  /* compute the last identical different entry */
  for (i = 1; i < old_depth && i < new_depth; i++)
    if (old_stack [i] != new_stack [i])
      break;
  last_same = i - 1;

  /* Memorize the previously selected item to be able to refresh it */
  old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
  if (old_selection && !old_selection->enabled)
    old_selection = NULL;
  new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
  if (new_selection && !new_selection->enabled)
    new_selection = NULL;

  /* Call callback when the hightlighted item changes.  */
  if (old_selection || new_selection)
    XtCallCallbackList ((Widget)mw, mw->menu.highlight,
			(XtPointer) new_selection);

  /* updates old_state from new_state.  It has to be done now because
     display_menu (called below) uses the old_stack to know what to display. */
  for (i = last_same + 1; i < new_depth; i++)
    old_stack [i] = new_stack [i];
  mw->menu.old_depth = new_depth;

  /* refresh the last selection */
  selection_position.x = 0;
  selection_position.y = 0;
  display_menu (mw, last_same, new_selection == old_selection,
		&selection_position, NULL, NULL, old_selection, new_selection);

  /* Now place the new menus.  */
  for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
    {
      window_state *previous_ws = &windows[i - 1];
      window_state *ws = &windows[i];

      ws->x = (previous_ws->x + selection_position.x
	       + mw->menu.shadow_thickness);
      if (mw->menu.horizontal && i == 1)
	ws->x += mw->menu.margin;

#if 0
      if (!mw->menu.horizontal || i > 1)
	ws->x += mw->menu.shadow_thickness;
#endif

      ws->y = (previous_ws->y + selection_position.y
	       + mw->menu.shadow_thickness);
      if (mw->menu.horizontal && i == 1)
	ws->y += mw->menu.margin;

      size_menu (mw, i);

      fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);

      XClearWindow (XtDisplay (mw), ws->window);
      XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
			 ws->width, ws->height);
      XMapRaised (XtDisplay (mw), ws->window);
      display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
    }

  /* unmap the menus that popped down */
  for (i = new_depth - 1; i < old_depth; i++)
    if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
      XUnmapWindow (XtDisplay (mw), windows[i].window);
}

static Boolean
motion_event_is_in_menu (mw, ev, level, relative_pos)
     XlwMenuWidget mw;
     XMotionEvent* ev;
     int level;
     XPoint* relative_pos;
{
  window_state* ws = &mw->menu.windows [level];
  int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
  int x = ws->x + shadow;
  int y = ws->y + shadow;
  relative_pos->x = ev->x_root - x;
  relative_pos->y = ev->y_root - y;
  return (x - shadow < ev->x_root && ev->x_root < x + ws->width
          && y - shadow < ev->y_root && ev->y_root < y + ws->height);
}

static Boolean
map_event_to_widget_value (mw, ev, val, level)
     XlwMenuWidget mw;
     XMotionEvent* ev;
     widget_value** val;
     int* level;
{
  int 		i;
  XPoint	relative_pos;
  window_state*	ws;
  int inside = 0;

  *val = NULL;

  /* Find the window */
  for (i = mw->menu.old_depth - 1; i >= 0; i--)
    {
      ws = &mw->menu.windows [i];
      if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
	{
          inside = 1;
	  display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL);

	  if (*val)
	    {
	      *level = i + 1;
	      return True;
	    }
	}
    }

  if (!inside) 
    {
      if (mw->menu.inside_entry != NULL) 
        XtCallCallbackList ((Widget)mw, mw->menu.leave,
                            (XtPointer) mw->menu.inside_entry);
      mw->menu.inside_entry = NULL;
    }

  return False;
}

/* Procedures */
static void
make_drawing_gcs (mw)
     XlwMenuWidget mw;
{
  XGCValues xgcv;
  float scale;
  XtGCMask mask = GCForeground | GCBackground;

#ifdef HAVE_X_I18N
  if (!mw->menu.fontSet)
    {
      xgcv.font = mw->menu.font->fid;
      mask |= GCFont;
    }
#else
  xgcv.font = mw->menu.font->fid;
  mask |= GCFont;
#endif
  xgcv.foreground = mw->menu.foreground;
  xgcv.background = mw->core.background_pixel;
  mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);

  xgcv.foreground = mw->menu.button_foreground;
  mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);

  xgcv.background = mw->core.background_pixel;

#define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))

  /* Allocate color for disabled menu-items.  */
  mw->menu.disabled_foreground = mw->menu.foreground;
  if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
    scale = 2.3;
  else
    scale = 0.55;

  x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
				    mw->core.colormap,
				    &mw->menu.disabled_foreground,
				    scale,
				    0x8000);

  if (mw->menu.foreground == mw->menu.disabled_foreground
      || mw->core.background_pixel == mw->menu.disabled_foreground)
    {
      /* Too few colors, use stipple.  */
      xgcv.foreground = mw->menu.foreground;
      xgcv.fill_style = FillStippled;
      xgcv.stipple = mw->menu.gray_pixmap;
      mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
				      | GCFillStyle | GCStipple, &xgcv);
    }
  else
    {
      /* Many colors available, use disabled pixel.  */
      xgcv.foreground = mw->menu.disabled_foreground;
      mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
    }

  xgcv.foreground = mw->menu.button_foreground;
  xgcv.background = mw->core.background_pixel;
  xgcv.fill_style = FillStippled;
  xgcv.stipple = mw->menu.gray_pixmap;
  mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
					 | GCFillStyle | GCStipple, &xgcv);

  xgcv.foreground = mw->core.background_pixel;
  xgcv.background = mw->menu.foreground;
  mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
}

static void
release_drawing_gcs (mw)
     XlwMenuWidget mw;
{
  XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
  XtReleaseGC ((Widget) mw, mw->menu.button_gc);
  XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
  XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
  XtReleaseGC ((Widget) mw, mw->menu.background_gc);
  /* let's get some segvs if we try to use these... */
  mw->menu.foreground_gc = (GC) -1;
  mw->menu.button_gc = (GC) -1;
  mw->menu.disabled_gc = (GC) -1;
  mw->menu.inactive_button_gc = (GC) -1;
  mw->menu.background_gc = (GC) -1;
}

#define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
		   ? ((unsigned long) (x)) : ((unsigned long) (y)))

static void
make_shadow_gcs (mw)
     XlwMenuWidget mw;
{
  XGCValues xgcv;
  unsigned long pm = 0;
  Display *dpy = XtDisplay ((Widget) mw);
  Screen *screen = XtScreen ((Widget) mw);
  Colormap cmap = mw->core.colormap;
  XColor topc, botc;
  int top_frobbed = 0, bottom_frobbed = 0;

  mw->menu.free_top_shadow_color_p = 0;
  mw->menu.free_bottom_shadow_color_p = 0;

  if (mw->menu.top_shadow_color == -1)
    mw->menu.top_shadow_color = mw->core.background_pixel;
  else
    mw->menu.top_shadow_color = mw->menu.top_shadow_color;

  if (mw->menu.bottom_shadow_color == -1)
    mw->menu.bottom_shadow_color = mw->menu.foreground;
  else
    mw->menu.bottom_shadow_color = mw->menu.bottom_shadow_color;

  if (mw->menu.top_shadow_color == mw->core.background_pixel ||
      mw->menu.top_shadow_color == mw->menu.foreground)
    {
      topc.pixel = mw->core.background_pixel;
#ifdef emacs
      if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
					    &topc.pixel,
					    1.2, 0x8000))
#else
      XQueryColor (dpy, cmap, &topc);
      /* don't overflow/wrap! */
      topc.red   = MINL (65535, topc.red   * 1.2);
      topc.green = MINL (65535, topc.green * 1.2);
      topc.blue  = MINL (65535, topc.blue  * 1.2);
      if (XAllocColor (dpy, cmap, &topc))
#endif
	{
	  mw->menu.top_shadow_color = topc.pixel;
	  mw->menu.free_top_shadow_color_p = 1;
	  top_frobbed = 1;
	}
    }
  if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
      mw->menu.bottom_shadow_color == mw->core.background_pixel)
    {
      botc.pixel = mw->core.background_pixel;
#ifdef emacs
      if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
					    &botc.pixel,
					    0.6, 0x4000))
#else
      XQueryColor (dpy, cmap, &botc);
      botc.red   *= 0.6;
      botc.green *= 0.6;
      botc.blue  *= 0.6;
      if (XAllocColor (dpy, cmap, &botc))
#endif
	{
	  mw->menu.bottom_shadow_color = botc.pixel;
	  mw->menu.free_bottom_shadow_color_p = 1;
	  bottom_frobbed = 1;
	}
    }

  if (top_frobbed && bottom_frobbed)
    {
      if (topc.pixel == botc.pixel)
	{
	  if (botc.pixel == mw->menu.foreground)
	    {
	      if (mw->menu.free_top_shadow_color_p)
		{
		  x_free_dpy_colors (dpy, screen, cmap,
				     &mw->menu.top_shadow_color, 1);
		  mw->menu.free_top_shadow_color_p = 0;
		}
	      mw->menu.top_shadow_color = mw->core.background_pixel;
	    }
	  else
	    {
	      if (mw->menu.free_bottom_shadow_color_p)
		{
		  x_free_dpy_colors (dpy, screen, cmap,
				     &mw->menu.bottom_shadow_color, 1);
		  mw->menu.free_bottom_shadow_color_p = 0;
		}
	      mw->menu.bottom_shadow_color = mw->menu.foreground;
	    }
	}
    }

  if (!mw->menu.top_shadow_pixmap &&
      mw->menu.top_shadow_color == mw->core.background_pixel)
    {
      mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
      if (mw->menu.free_top_shadow_color_p)
	{
	  x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
	  mw->menu.free_top_shadow_color_p = 0;
	}
      mw->menu.top_shadow_color = mw->menu.foreground;
    }
  if (!mw->menu.bottom_shadow_pixmap &&
      mw->menu.bottom_shadow_color == mw->core.background_pixel)
    {
      mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
      if (mw->menu.free_bottom_shadow_color_p)
	{
	  x_free_dpy_colors (dpy, screen, cmap,
			     &mw->menu.bottom_shadow_color, 1);
	  mw->menu.free_bottom_shadow_color_p = 0;
	}
      mw->menu.bottom_shadow_color = mw->menu.foreground;
    }

  xgcv.fill_style = FillStippled;
  xgcv.foreground = mw->menu.top_shadow_color;
  xgcv.stipple = mw->menu.top_shadow_pixmap;
  pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
  mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);

  xgcv.foreground = mw->menu.bottom_shadow_color;
  xgcv.stipple = mw->menu.bottom_shadow_pixmap;
  pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
  mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
}


static void
release_shadow_gcs (mw)
     XlwMenuWidget mw;
{
  Display *dpy = XtDisplay ((Widget) mw);
  Screen *screen = XtScreen ((Widget) mw);
  Colormap cmap = mw->core.colormap;
  Pixel px[2];
  int i = 0;

  if (mw->menu.free_top_shadow_color_p)
    px[i++] = mw->menu.top_shadow_color;
  if (mw->menu.free_bottom_shadow_color_p)
    px[i++] = mw->menu.bottom_shadow_color;
  if (i > 0)
    x_free_dpy_colors (dpy, screen, cmap, px, i);

  XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
  XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
}

static void
XlwMenuInitialize (request, mw, args, num_args)
     Widget request;
     XlwMenuWidget mw;
     ArgList args;
     Cardinal *num_args;
{
  /* Get the GCs and the widget size */

  Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  Display* display = XtDisplay (mw);

#if 0
  widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));

  /* _XtCreate is freeing the object that was passed to us,
     so make a copy that we will actually keep.  */
  lwlib_bcopy (mw->menu.contents, tem, sizeof (widget_value));
  mw->menu.contents = tem;
#endif

/*  mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
  mw->menu.cursor = mw->menu.cursor_shape;

  mw->menu.gray_pixmap
    = XCreatePixmapFromBitmapData (display, window, gray_bitmap_bits,
				   gray_bitmap_width, gray_bitmap_height,
				   (unsigned long)1, (unsigned long)0, 1);

  /* I don't understand why this ends up 0 sometimes,
     but it does.  This kludge works around it.
     Can anyone find a real fix?   -- rms.  */
  if (mw->menu.font == 0)
    mw->menu.font = xlwmenu_default_font;
#ifdef HAVE_X_I18N
  if (mw->menu.fontSet)
    mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
#endif
      
  make_drawing_gcs (mw);
  make_shadow_gcs (mw);

  mw->menu.popped_up = False;

  mw->menu.old_depth = 1;
  mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
  mw->menu.old_stack_length = 1;
  mw->menu.old_stack [0] = mw->menu.contents;

  mw->menu.new_depth = 0;
  mw->menu.new_stack = 0;
  mw->menu.new_stack_length = 0;
  push_new_stack (mw, mw->menu.contents);

  mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
  mw->menu.windows_length = 1;
  mw->menu.windows [0].x = 0;
  mw->menu.windows [0].y = 0;
  mw->menu.windows [0].width = 0;
  mw->menu.windows [0].height = 0;
  size_menu (mw, 0);

  mw->core.width = mw->menu.windows [0].width;
  mw->core.height = mw->menu.windows [0].height;
}

static void
XlwMenuClassInitialize ()
{
}

static void
XlwMenuRealize (w, valueMask, attributes)
     Widget w;
     Mask *valueMask;
     XSetWindowAttributes *attributes;
{
  XlwMenuWidget mw = (XlwMenuWidget)w;
  XSetWindowAttributes xswa;
  int mask;

  (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
    (w, valueMask, attributes);

  xswa.save_under = True;
  xswa.cursor = mw->menu.cursor_shape;
  mask = CWSaveUnder | CWCursor;
  XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);

  mw->menu.windows [0].window = XtWindow (w);
  mw->menu.windows [0].x = w->core.x;
  mw->menu.windows [0].y = w->core.y;
  mw->menu.windows [0].width = w->core.width;
  mw->menu.windows [0].height = w->core.height;
}

/* Only the toplevel menubar/popup is a widget so it's the only one that
   receives expose events through Xt.  So we repaint all the other panes
   when receiving an Expose event. */
static void
XlwMenuRedisplay (w, ev, region)
     Widget w;
     XEvent* ev;
     Region region;
{
  XlwMenuWidget mw = (XlwMenuWidget)w;
  int i;

  /* If we have a depth beyond 1, it's because a submenu was displayed.
     If the submenu has been destroyed, set the depth back to 1.  */
  if (submenu_destroyed)
    {
      mw->menu.old_depth = 1;
      submenu_destroyed = 0;
    }

  for (i = 0; i < mw->menu.old_depth; i++)
    display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
}


/* Part of a hack to make the menu redisplay when a tooltip frame
   over a menu item is unmapped.  */

void
xlwmenu_redisplay (w)
     Widget w;
{
  XlwMenuRedisplay (w, NULL, None);
}

static void
XlwMenuDestroy (w)
     Widget w;
{
  int i;
  XlwMenuWidget mw = (XlwMenuWidget) w;

  if (pointer_grabbed)
    ungrab_all ((Widget)w, CurrentTime);
  pointer_grabbed = 0;

  submenu_destroyed = 1;

  release_drawing_gcs (mw);
  release_shadow_gcs (mw);

  /* this doesn't come from the resource db but is created explicitly
     so we must free it ourselves. */
  XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
  mw->menu.gray_pixmap = (Pixmap) -1;

#if 0
  /* Do free mw->menu.contents because nowadays we copy it
     during initialization.  */
  XtFree (mw->menu.contents);
#endif

  /* Don't free mw->menu.contents because that comes from our creator.
     The `*_stack' elements are just pointers into `contents' so leave
     that alone too.  But free the stacks themselves. */
  if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
  if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);

  /* Remember, you can't free anything that came from the resource
     database.  This includes:
         mw->menu.cursor
         mw->menu.top_shadow_pixmap
         mw->menu.bottom_shadow_pixmap
         mw->menu.font
     Also the color cells of top_shadow_color, bottom_shadow_color,
     foreground, and button_foreground will never be freed until this
     client exits.  Nice, eh?
   */

  /* start from 1 because the one in slot 0 is w->core.window */
  for (i = 1; i < mw->menu.windows_length; i++)
    XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
  if (mw->menu.windows)
    XtFree ((char *) mw->menu.windows);
}

static Boolean
XlwMenuSetValues (current, request, new)
     Widget current;
     Widget request;
     Widget new;
{
  XlwMenuWidget oldmw = (XlwMenuWidget)current;
  XlwMenuWidget newmw = (XlwMenuWidget)new;
  Boolean redisplay = False;
  int i;

  if (newmw->menu.contents
      && newmw->menu.contents->contents
      && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
    redisplay = True;
  /* Do redisplay if the contents are entirely eliminated.  */
  if (newmw->menu.contents
      && newmw->menu.contents->contents == 0
      && newmw->menu.contents->change >= VISIBLE_CHANGE)
    redisplay = True;

  if (newmw->core.background_pixel != oldmw->core.background_pixel
      || newmw->menu.foreground != oldmw->menu.foreground
#ifdef HAVE_X_I18N
      || newmw->menu.fontSet != oldmw->menu.fontSet
      || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
#else
      || newmw->menu.font != oldmw->menu.font
#endif
      )
    {
      release_drawing_gcs (newmw);
      make_drawing_gcs (newmw);

      release_shadow_gcs (newmw);
      /* Cause the shadow colors to be recalculated.  */
      newmw->menu.top_shadow_color = -1;
      newmw->menu.bottom_shadow_color = -1;
      make_shadow_gcs (newmw);

      redisplay = True;

      if (XtIsRealized (current))
	/* If the menu is currently displayed, change the display.  */
	for (i = 0; i < oldmw->menu.windows_length; i++)
	  {
	    XSetWindowBackground (XtDisplay (oldmw),
				  oldmw->menu.windows [i].window,
				  newmw->core.background_pixel);
	    /* clear windows and generate expose events */
	    XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
			0, 0, 0, 0, True);
	  }
    }

#ifdef HAVE_X_I18N
  if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
    {
      redisplay = True;
      newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
    }
#endif

  return redisplay;
}

static void
XlwMenuResize (w)
     Widget w;
{
  XlwMenuWidget mw = (XlwMenuWidget)w;

  if (mw->menu.popped_up)
    {
      /* Don't allow the popup menu to resize itself.  */
      mw->core.width = mw->menu.windows [0].width;
      mw->core.height = mw->menu.windows [0].height;
      mw->core.parent->core.width = mw->core.width ;
      mw->core.parent->core.height = mw->core.height ;
    }
  else
    {
      mw->menu.windows [0].width = mw->core.width;
      mw->menu.windows [0].height = mw->core.height;
    }
}

/* Action procedures */
static void
handle_single_motion_event (mw, ev)
     XlwMenuWidget mw;
     XMotionEvent* ev;
{
  widget_value*	val;
  int 		level;

  if (!map_event_to_widget_value (mw, ev, &val, &level))
    pop_new_stack_if_no_contents (mw);
  else
    set_new_state (mw, val, level);
  remap_menubar (mw);

  /* Sync with the display.  Makes it feel better on X terms. */
  XSync (XtDisplay (mw), False);
}

static void
handle_motion_event (mw, ev)
     XlwMenuWidget mw;
     XMotionEvent* ev;
{
  int x = ev->x_root;
  int y = ev->y_root;
  int state = ev->state;

  handle_single_motion_event (mw, ev);

  /* allow motion events to be generated again */
  if (ev->is_hint
      && XQueryPointer (XtDisplay (mw), ev->window,
			&ev->root, &ev->subwindow,
			&ev->x_root, &ev->y_root,
			&ev->x, &ev->y,
			&ev->state)
      && ev->state == state
      && (ev->x_root != x || ev->y_root != y))
    handle_single_motion_event (mw, ev);
}

static void
Start (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
  XlwMenuWidget mw = (XlwMenuWidget)w;

  if (!mw->menu.popped_up)
    {
      menu_post_event = *ev;
      /* If event is set to CurrentTime, get the last known time stamp.
         This is for calculating if (popup) menus should stay up after
         a fast click.  */
      if (menu_post_event.xbutton.time == CurrentTime)
        menu_post_event.xbutton.time
          = XtLastTimestampProcessed (XtDisplay (w));

      pop_up_menu (mw, (XButtonPressedEvent*) ev);
    }
  else
    {
      /* If we push a button while the menu is posted semipermanently,
	 releasing the button should always pop the menu down.  */
      next_release_must_exit = 1;

      /* notes the absolute position of the menubar window */
      mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
      mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;

      /* handles the down like a move, slots are compatible */
      ev->xmotion.is_hint = 0;
      handle_motion_event (mw, &ev->xmotion);
    }
}

static void
Drag (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
  XlwMenuWidget mw = (XlwMenuWidget)w;
  if (mw->menu.popped_up)
    handle_motion_event (mw, &ev->xmotion);
}

/* Do nothing.
   This is how we handle presses and releases of modifier keys.  */
static void
Nothing (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
}

static widget_value *
find_first_selectable (mw, item, skip_titles)
     XlwMenuWidget mw;
     widget_value *item;
     int skip_titles;
{
  widget_value *current = item;
  enum menu_separator separator;

  while (lw_separator_p (current->name, &separator, 0) || !current->enabled
         || (skip_titles && !current->call_data && !current->contents))
    if (current->next)
      current=current->next;
    else
      return NULL;

  return current;
}

static widget_value *
find_next_selectable (mw, item, skip_titles)
     XlwMenuWidget mw;
     widget_value *item;
     int skip_titles;
{
  widget_value *current = item;
  enum menu_separator separator;

  while (current->next && (current=current->next) &&
	 (lw_separator_p (current->name, &separator, 0) || !current->enabled
          || (skip_titles && !current->call_data && !current->contents)))
    ;

  if (current == item)
    {
      if (mw->menu.old_depth < 2)
	return current;
      current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;

      while (lw_separator_p (current->name, &separator, 0)
             || !current->enabled
             || (skip_titles && !current->call_data
                 && !current->contents))
	{
	  if (current->next)
	    current=current->next;

	  if (current == item)
	    break;
	}

    }

  return current;
}

static widget_value *
find_prev_selectable (mw, item, skip_titles)
     XlwMenuWidget mw;
     widget_value *item;
     int skip_titles;
{
  widget_value *current = item;
  widget_value *prev = item;

  while ((current=find_next_selectable (mw, current, skip_titles))
         != item)
    {
      if (prev == current)
	break;
      prev=current;
    }

  return prev;
}

static void
Down (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
  XlwMenuWidget mw = (XlwMenuWidget) w;
  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  int popup_menu_p = mw->menu.top_depth == 1;

  /* Inside top-level menu-bar?  */
  if (mw->menu.old_depth == mw->menu.top_depth)
    /* When <down> in the menu-bar is pressed, display the corresponding
       sub-menu and select the first selectable menu item there.
       If this is a popup menu, skip title item of the popup.  */
    set_new_state (mw,
                   find_first_selectable (mw,
                                          selected_item->contents,
                                          popup_menu_p),
                   mw->menu.old_depth);
  else
    /* Highlight next possible (enabled and not separator) menu item.  */
    set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
                   mw->menu.old_depth - 1);

  remap_menubar (mw);
}

static void
Up (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
  XlwMenuWidget mw = (XlwMenuWidget) w;
  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  int popup_menu_p = mw->menu.top_depth == 1;

  /* Inside top-level menu-bar?  */
  if (mw->menu.old_depth == mw->menu.top_depth)
    {
      /* FIXME: this is tricky.  <up> in the menu-bar should select the
	 last selectable item in the list.  So we select the first
	 selectable one and find the previous selectable item.  Is there
	 a better way?  */
      /* If this is a popup menu, skip title item of the popup.  */
      set_new_state (mw,
                     find_first_selectable (mw,
                                            selected_item->contents,
                                            popup_menu_p),
                     mw->menu.old_depth);
      remap_menubar (mw);
      selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
      set_new_state (mw,
                     find_prev_selectable (mw,
                                           selected_item,
                                           popup_menu_p),
                     mw->menu.old_depth - 1);
    }
  else
    /* Highlight previous (enabled and not separator) menu item.  */
    set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
                   mw->menu.old_depth - 1);

  remap_menubar (mw);
}

void
Left (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
  XlwMenuWidget mw = (XlwMenuWidget) w;
  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];

  /* Inside top-level menu-bar?  */
  if (mw->menu.old_depth == mw->menu.top_depth)
    /* When <left> in the menu-bar is pressed, display the previous item on
       the menu-bar. If the current item is the first one, highlight the
       last item in the menubar (probably Help).  */
    set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
                   mw->menu.old_depth - 1);
  else if (mw->menu.old_depth == 1
	   && selected_item->contents)     /* Is this menu item expandable?  */
    {
      set_new_state (mw, selected_item->contents, mw->menu.old_depth);
      remap_menubar (mw);
      selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
      if (!selected_item->enabled && find_first_selectable (mw,
                                                            selected_item,
                                                            0))
	set_new_state (mw, find_first_selectable (mw, selected_item, 0),
                       mw->menu.old_depth - 1);
    }

  else
    {
      pop_new_stack_if_no_contents (mw);
      set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
                     mw->menu.old_depth - 2);
    }

  remap_menubar (mw);
}

void
Right (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
  XlwMenuWidget mw = (XlwMenuWidget) w;
  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];

  /* Inside top-level menu-bar?  */
  if (mw->menu.old_depth == mw->menu.top_depth)
    /* When <right> in the menu-bar is pressed, display the next item on
       the menu-bar. If the current item is the last one, highlight the
       first item (probably File).  */
    set_new_state (mw, find_next_selectable (mw, selected_item, 0),
                   mw->menu.old_depth - 1);
  else if (selected_item->contents)     /* Is this menu item expandable?  */
    {
      set_new_state (mw, selected_item->contents, mw->menu.old_depth);
      remap_menubar (mw);
      selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
      if (!selected_item->enabled && find_first_selectable (mw,
                                                            selected_item,
                                                            0))
	set_new_state (mw, find_first_selectable (mw, selected_item, 0),
                       mw->menu.old_depth - 1);
    }
  else
    {
      pop_new_stack_if_no_contents (mw);
      set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
                     mw->menu.old_depth - 2);
    }

  remap_menubar (mw);
}

/* Handle key press and release events while menu is popped up.
   Our action is to get rid of the menu.  */
static void
Key (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
  XlwMenuWidget mw = (XlwMenuWidget)w;

  /* Pop down everything.  */
  mw->menu.new_depth = 1;
  remap_menubar (mw);

  if (mw->menu.popped_up)
    {
      mw->menu.popped_up = False;
      ungrab_all ((Widget)mw, ev->xmotion.time);
      if (XtIsShell (XtParent ((Widget) mw)))
	XtPopdown (XtParent ((Widget) mw));
      else
	{
	  XtRemoveGrab ((Widget) mw);
	  display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
	}
    }

  /* callback */
  XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
}

static void
Select (w, ev, params, num_params)
     Widget w;
     XEvent *ev;
     String *params;
     Cardinal *num_params;
{
  XlwMenuWidget mw = (XlwMenuWidget)w;
  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];

  /* If user releases the button quickly, without selecting anything,
     after the initial down-click that brought the menu up,
     do nothing.  */
  if ((selected_item == 0
       || ((widget_value *) selected_item)->call_data == 0)
      && !next_release_must_exit
      && (ev->xbutton.time - menu_post_event.xbutton.time
	  < XtGetMultiClickTime (XtDisplay (w))))
    return;

  /* pop down everything.  */
  mw->menu.new_depth = 1;
  remap_menubar (mw);

  if (mw->menu.popped_up)
    {
      mw->menu.popped_up = False;
      ungrab_all ((Widget)mw, ev->xmotion.time);
      if (XtIsShell (XtParent ((Widget) mw)))
	XtPopdown (XtParent ((Widget) mw));
      else
	{
	  XtRemoveGrab ((Widget) mw);
	  display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
	}
    }

  /* callback */
  XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
}


/* Special code to pop-up a menu */
static void
pop_up_menu (mw, event)
     XlwMenuWidget mw;
     XButtonPressedEvent* event;
{
  int		x = event->x_root;
  int		y = event->y_root;
  int		w;
  int		h;
  int		borderwidth = mw->menu.shadow_thickness;
  Screen*	screen = XtScreen (mw);
  Display       *display = XtDisplay (mw);

  next_release_must_exit = 0;

  mw->menu.inside_entry = NULL;
  XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);

  if (XtIsShell (XtParent ((Widget)mw)))
    size_menu (mw, 0);

  w = mw->menu.windows [0].width;
  h = mw->menu.windows [0].height;

  x -= borderwidth;
  y -= borderwidth;
  if (x < borderwidth)
    x = borderwidth;
  if (x + w + 2 * borderwidth > WidthOfScreen (screen))
    x = WidthOfScreen (screen) - w - 2 * borderwidth;
  if (y < borderwidth)
    y = borderwidth;
  if (y + h + 2 * borderwidth> HeightOfScreen (screen))
    y = HeightOfScreen (screen) - h - 2 * borderwidth;

  mw->menu.popped_up = True;
  if (XtIsShell (XtParent ((Widget)mw)))
    {
      XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
			 XtParent ((Widget)mw)->core.border_width);
      XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
      display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
      mw->menu.windows [0].x = x + borderwidth;
      mw->menu.windows [0].y = y + borderwidth;
      mw->menu.top_depth = 1;  /* Popup menus don't have a bar so top is 1  */
    }
  else
    {
      XEvent *ev = (XEvent *) event;

      XtAddGrab ((Widget) mw, True, True);

      /* notes the absolute position of the menubar window */
      mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
      mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
      mw->menu.top_depth = 2;
    }

#ifdef emacs
  x_catch_errors (display);
#endif
  if (XtGrabPointer ((Widget)mw, False,
                     (PointerMotionMask
                      | PointerMotionHintMask
                      | ButtonReleaseMask
                      | ButtonPressMask),
                     GrabModeAsync, GrabModeAsync, None,
                     mw->menu.cursor_shape,
                     event->time) == Success)
    {
      if (! GRAB_KEYBOARD
          || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
                             GrabModeAsync, event->time) == Success)
        {
          XtSetKeyboardFocus((Widget)mw, None);
          pointer_grabbed = 1;
        }
      else
        XtUngrabPointer ((Widget)mw, event->time);
    }

#ifdef emacs
  if (x_had_errors_p (display))
    {
      pointer_grabbed = 0;
      XtUngrabPointer ((Widget)mw, event->time);
    }
  x_uncatch_errors ();
#endif

  ((XMotionEvent*)event)->is_hint = 0;
  handle_motion_event (mw, (XMotionEvent*)event);
}

/* arch-tag: 657f43dd-dfd0-4cc9-910c-52935f01176e
   (do not change this comment) */