home *** CD-ROM | disk | FTP | other *** search
Wrap
/* windows.c -- System-independant window handling Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk> This file is part of Jade. Jade 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. Jade 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 Jade; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "jade.h" #include "jade_protos.h" #include <stdarg.h> #include <stdlib.h> #include <string.h> _PR void messagen(u_char *, int); _PR void message(u_char *); _PR void messagef(u_char *, ...); _PR void no_message(VW *); _PR void std_message(VW *); _PR void reset_message(VW *); _PR void refresh_message(VW *); _PR void windows_init(void); _PR void windows_kill(void); _PR void window_sweep(void); _PR void window_prin(VALUE, VALUE); VALUE sym_make_window_hook, sym_destroy_window_hook; /* This can contain `dead' windows, ie vw_Window==NULL, they have been close'd but must hang around until we're sure all refs are dead. */ _PR VW *view_chain; /* curr_vw is the active window */ _PR VW *curr_vw; _PR int window_count; _PR bool log_messages; VW *view_chain; VW *curr_vw; int window_count; bool log_messages; _PR short def_dims[4]; short def_dims[4] = { 0, 0, 80, 24 }; static void copy_prefs(VW *dest, VW *src) { if(src) { dest->vw_MaxScroll = src->vw_MaxScroll; dest->vw_XStepRatio = src->vw_XStepRatio; dest->vw_YStepRatio = src->vw_YStepRatio; dest->vw_Flags = src->vw_Flags; dest->vw_FontName = src->vw_FontName; #ifdef HAVE_AMIGA dest->vw_WindowSys.ws_FontSize = src->vw_WindowSys.ws_FontSize; dest->vw_WindowSys.ws_ScreenName = src->vw_WindowSys.ws_ScreenName; #endif } else { dest->vw_MaxScroll = 20; dest->vw_XStepRatio = 4; #ifdef HAVE_AMIGA dest->vw_YStepRatio = 0; #else dest->vw_YStepRatio = 4; #endif dest->vw_Flags = 0; dest->vw_FontName = def_font_str; #ifdef HAVE_AMIGA dest->vw_WindowSys.ws_FontSize = ami_def_font_size; dest->vw_WindowSys.ws_ScreenName = ami_def_pub_screen; #endif } } _PR VALUE cmd_make_window(VALUE xv, VALUE yv, VALUE wv, VALUE hv); DEFUN("make-window", cmd_make_window, subr_make_window, (VALUE xv, VALUE yv, VALUE wv, VALUE hv), V_Subr4, DOC_make_window) /* ::doc:make_window:: make-window [X] [Y] [WIDTH] [HEIGHT] Return a new window, it will be displaying the same buffer as the currently active window. ::end:: */ { VW *vw; TX *tx = curr_vw ? curr_vw->vw_Tx : NULL; if(NUMBERP(xv)) def_dims[0] = VNUM(xv); if(NUMBERP(yv)) def_dims[1] = VNUM(yv); if(NUMBERP(wv)) def_dims[2] = VNUM(wv); if(NUMBERP(hv)) def_dims[3] = VNUM(hv); vw = mycalloc(sizeof(VW)); if(vw) { vw->vw_Type = V_Window; vw->vw_Next = view_chain; view_chain = vw; copy_prefs(vw, curr_vw); if(sys_set_font(vw)) { vw->vw_Window = sys_new_window(curr_vw, vw, TRUE); if(vw->vw_Window) { window_count++; sys_new_vw(vw); vw->vw_BlockStatus = -1; vw->vw_BufferList = sym_nil; sys_update_dimensions(vw); if(tx) { vw->vw_Tx = tx; vw->vw_CursorPos = tx->tx_SavedCPos; vw->vw_StartLine = tx->tx_SavedWPos.pos_Line; vw->vw_StartCol = tx->tx_SavedWPos.pos_Col; std_message(vw); cmd_eval_hook2(sym_make_window_hook, VAL(vw)); #ifndef NOSCRLBAR sys_update_scroller(vw); #endif } vw->vw_Flags |= VWFF_FORCE_REFRESH; return(VAL(vw)); } sys_unset_font(vw); } myfree(vw); } return(NULL); } _PR VALUE cmd_destroy_window(VALUE win); DEFUN("destroy-window", cmd_destroy_window, subr_destroy_window, (VALUE win), V_Subr1, DOC_destroy_window) /* ::doc:destroy_window:: destroy-window [WINDOW] Close WINDOW (or the current window), if this was the last one all files in memory are flushed and jade will exit. ::end:: */ { VW *vw = WINDOWP(win) ? VWIN(win) : curr_vw; cmd_eval_hook2(sym_destroy_window_hook, VAL(vw)); no_message(vw); /* This function is to take care of OS-independant stuff: releasing GCs etc... */ sys_kill_vw(vw); sys_kill_window(vw); sys_unset_font(vw); window_count--; /* This flags that this window is dead. */ vw->vw_Window = WINDOW_NIL; vw->vw_Tx = NULL; vw->vw_BufferList = sym_nil; if(curr_vw == vw) { while((vw = vw->vw_Next)) { if(vw->vw_Window) { curr_vw = vw; return(VAL(vw)); } } vw = view_chain; while(vw && (vw != curr_vw)) { if(vw->vw_Window) { curr_vw = vw; return(VAL(vw)); } vw = vw->vw_Next; } /* No living windows left :-( we'll die soon :-( */ curr_vw = NULL; throw_value = cmd_cons(sym_quit, make_number(0)); /* experimental. */ return(NULL); } return(VAL(curr_vw)); } _PR VALUE cmd_sleep_window(VALUE vw); DEFUN_INT("sleep-window", cmd_sleep_window, subr_sleep_window, (VALUE vw), V_Subr1, DOC_sleep_window, "") /* ::doc:sleep_window:: sleep-window [WINDOW] Iconifies the current window. ::end:: */ { if(!WINDOWP(vw)) vw = VAL(curr_vw); if(((VWIN(vw)->vw_Flags & VWFF_SLEEPING) == 0) && sys_sleep_vw(VWIN(vw))) return(vw); return(sym_nil); } _PR VALUE cmd_unsleep_window(VALUE vw); DEFUN_INT("unsleep-window", cmd_unsleep_window, subr_unsleep_window, (VALUE vw), V_Subr1, DOC_unsleep_window, "") /* ::doc:unsleep_window:: unsleep-window [WINDOW] Uniconifies the current window. ::end:: */ { if(!WINDOWP(vw)) vw = VAL(curr_vw); if((VWIN(vw)->vw_Flags & VWFF_SLEEPING) && sys_unsleep_vw(VWIN(vw))) return(vw); return(sym_nil); } _PR VALUE cmd_next_window(VALUE vw, VALUE activ); DEFUN_INT("next-window", cmd_next_window, subr_next_window, (VALUE vw, VALUE activ), V_Subr2, DOC_next_window, "!\np") /* ::doc:next_window:: next-window [WINDOW] [ACTIVATE] Cycles through the open windows forwards. ::end:: */ { if(!WINDOWP(vw)) vw = VAL(curr_vw->vw_Next); while(VWIN(vw) != curr_vw) { if(!vw) vw = VAL(view_chain); if(VWIN(vw)->vw_Window) { if(!NILP(activ)) { curr_vw = VWIN(vw); sys_activate_win(VWIN(vw)); } return(vw); } vw = VAL(VWIN(vw)->vw_Next); } return(VAL(curr_vw)); } void messagen(u_char *title, int length) { VW *vw = curr_vw; if(log_messages) { fwrite(title, 1, length, stderr); fputc('\n', stderr); } if((vw->vw_Flags & VWFF_SLEEPING) == 0) { str_free(vw->vw_Message); vw->vw_Message = str_dupn(title, length); vw->vw_MessageLen = length; vw->vw_Flags |= VWFF_REFRESH_STATUS | VWFF_MESSAGE; } } void message(u_char *msg) { messagen(msg, strlen(msg)); } void messagef(u_char *fmt, ...) { VW *vw = curr_vw; va_list args; if((vw->vw_Flags & VWFF_SLEEPING) == 0) { u_char fmtbuff[256]; u_long len; va_start(args, fmt); vsprintf(fmtbuff, fmt, args); va_end(args); if(log_messages) fprintf(stderr, "%s\n", fmtbuff); str_free(vw->vw_Message); len = strlen(fmtbuff); vw->vw_Message = str_dupn(fmtbuff, len); vw->vw_MessageLen = len; vw->vw_Flags |= VWFF_REFRESH_STATUS | VWFF_MESSAGE; } } void no_message(VW *vw) { if(((vw->vw_Flags & VWFF_SLEEPING) == 0) && vw->vw_Message) { str_free(vw->vw_Message); vw->vw_Message = NULL; vw->vw_MessageLen = 0; vw->vw_Flags &= ~VWFF_MESSAGE; vw->vw_Flags |= VWFF_REFRESH_STATUS; } } void std_message(VW *vw) { if(((vw->vw_Flags & VWFF_MESSAGE) == 0) && ((vw->vw_Flags & VWFF_SLEEPING) == 0)) { TX *tx = vw->vw_Tx; u_char *blk; u_char fmtbuff[100]; u_long len; if(vw->vw_BlockStatus >= 0) { if(vw->vw_BlockStatus == 0) blk = "B"; else blk = "b"; } else blk = ""; str_free(vw->vw_Message); calc_cursor_offset(vw); sprintf(fmtbuff, "%s%s %c%s%s%c (%ld,%ld) %ld line(s) %s", VSTR(tx->tx_BufferName), ((tx->tx_Changes != tx->tx_ProperSaveChanges) && (!(tx->tx_Flags & TXFF_SPECIAL))) ? "+" : (tx->tx_Flags & TXFF_RDONLY ? "-" : ""), (recurse_depth ? '[' : '('), (tx->tx_ModeName ? (char *)VSTR(tx->tx_ModeName) : "generic"), VSTR(tx->tx_MinorModeNameString), (recurse_depth ? ']' : ')'), vw->vw_LastCursorOffset + 1, vw->vw_CursorPos.pos_Line + 1, tx->tx_NumLines, blk); len = strlen(fmtbuff); vw->vw_Message = str_dupn(fmtbuff, len); vw->vw_MessageLen = len; vw->vw_Flags |= VWFF_REFRESH_STATUS; } } void reset_message(VW *vw) { vw->vw_Flags &= ~VWFF_MESSAGE; } void refresh_message(VW *vw) { if(vw->vw_Message) { int len = vw->vw_MessageLen; if(len >= vw->vw_MaxX) redraw_message(vw, vw->vw_Message + (len - vw->vw_MaxX) + 1, vw->vw_MaxX - 1); else redraw_message(vw, vw->vw_Message, len); } else redraw_message(vw, "", 0); } _PR VALUE cmd_message(VALUE string, VALUE now); DEFUN("message", cmd_message, subr_message, (VALUE string, VALUE now), V_Subr2, DOC_message) /* ::doc:message:: message STRING [DISPLAY-NOW] Temporarily sets the status display to STRING, this won't happen until the window is next refreshed unless DISPLAY-NOW is non-nil. ::end:: */ { DECLARE1(string, STRINGP); message(VSTR(string)); if(!NILP(now)) { refresh_message(curr_vw); curr_vw->vw_Flags &= ~VWFF_REFRESH_STATUS; #ifdef HAVE_X11 XFlush(x11_display); #endif } return(string); } _PR VALUE cmd_font_name(VALUE vw); DEFUN("font-name", cmd_font_name, subr_font_name, (VALUE vw), V_Subr1, DOC_font_name) /* ::doc:font_name:: font-name [WINDOW] Returns the name of the font being used in this window. ::end:: */ { if(!WINDOWP(vw)) vw = VAL(curr_vw); return(VWIN(vw)->vw_FontName); } _PR VALUE var_max_scroll(VALUE val); DEFUN("max-scroll", var_max_scroll, subr_max_scroll, (VALUE val), V_Var, DOC_max_scroll) /* ::doc:max_scroll:: Maximum scroll distance (number of lines). If a set of lines has to be scrolled further than this the whole window is redrawn. ::end:: */ { VW *vw = curr_vw; if(val) { if(NUMBERP(val)) vw->vw_MaxScroll = VNUM(val); return(NULL); } return(make_number(vw->vw_MaxScroll)); } _PR VALUE var_y_scroll_step_ratio(VALUE val); DEFUN("y-scroll-step-ratio", var_y_scroll_step_ratio, subr_y_scroll_step_ratio, (VALUE val), V_Var, DOC_y_scroll_step_ratio) /* ::doc:y_scroll_step_ratio:: Controls the actual number of lines scrolled when the cursor moves out of view. The number of lines to move the display origin is calcualted with the formula: LINES_TO_SCROLL = TOTAL_LINES_IN_WINDOW / y-scroll-step-ratio If the value is 0 then the window will be scrolled by one line. ::end:: */ { VW *vw = curr_vw; if(val) { if(NUMBERP(val)) { vw->vw_YStepRatio = VNUM(val); sys_update_dimensions(vw); } return(NULL); } return(make_number(vw->vw_YStepRatio)); } _PR VALUE var_x_scroll_step_ratio(VALUE val); DEFUN("x-scroll-step-ratio", var_x_scroll_step_ratio, subr_x_scroll_step_ratio, (VALUE val), V_Var, DOC_x_scroll_step_ratio) /* ::doc:x_scroll_step_ratio:: Controls the actual number of columns scrolled when the cursor moves out of view. The number of lines to move the display origin is calcualted with the formula: COLUMNS_TO_SCROLL = TOTAL_COLUMNS_IN_WINDOW / x-scroll-step-ratio If the value is 0 then the window will be scrolled by one column. ::end:: */ { VW *vw = curr_vw; if(val) { if(NUMBERP(val)) { vw->vw_XStepRatio = VNUM(val); sys_update_dimensions(vw); } return(NULL); } return(make_number(vw->vw_XStepRatio)); } _PR VALUE cmd_rect_blocks_p(VALUE vw); DEFUN("rect-blocks-p", cmd_rect_blocks_p, subr_rect_blocks_p, (VALUE vw), V_Subr1, DOC_rect_blocks_p) /* ::doc:rect_blocks_p:: rect-blocks-p [WINDOW] Returns t if blocks marked in WINDOW (or the current one) are treated as rectangles. ::end:: */ { if(!WINDOWP(vw)) vw = VAL(curr_vw); if(VWIN(vw)->vw_Flags & VWFF_RECTBLOCKS) return(sym_t); return(sym_nil); } _PR VALUE cmd_set_rect_blocks(VALUE vw, VALUE stat); DEFUN("set-rect-blocks", cmd_set_rect_blocks, subr_set_rect_blocks, (VALUE vw, VALUE stat), V_Subr2, DOC_set_rect_blocks) /* ::doc:set_rect_blocks:: set-rect-blocks WINDOW STATUS Controls whether or not blocks are taken as contiguous regions of text or as rectangles in WINDOW. When STATUS is t rectangles are used. ::end:: */ { int oflags; if(!WINDOWP(vw)) vw = VAL(curr_vw); oflags = VWIN(vw)->vw_Flags; if(NILP(stat)) VWIN(vw)->vw_Flags &= ~VWFF_RECTBLOCKS; else VWIN(vw)->vw_Flags |= VWFF_RECTBLOCKS; if((VWIN(vw)->vw_BlockStatus == 0) && (VWIN(vw)->vw_Flags != oflags)) set_block_refresh(VWIN(vw)); return(stat); } _PR VALUE cmd_window_asleep_p(void); DEFUN("window-asleep-p", cmd_window_asleep_p, subr_window_asleep_p, (void), V_Subr0, DOC_window_asleep_p) /* ::doc:window_asleep_p:: window-asleep-p Returns t if window is currently iconified. ::end:: */ { if(curr_vw->vw_Flags & VWFF_SLEEPING) return(sym_t); return(sym_nil); } _PR VALUE cmd_window_count(void); DEFUN("window-count", cmd_window_count, subr_window_count, (void), V_Subr0, DOC_window_count) /* ::doc:window_count:: window-count Number of opened windows. ::end:: */ { return(make_number(window_count)); } _PR VALUE cmd_position_window(VALUE left, VALUE top, VALUE width, VALUE height); DEFUN("position-window", cmd_position_window, subr_position_window, (VALUE left, VALUE top, VALUE width, VALUE height), V_Subr4, DOC_position_window) /* ::doc:position_window:: position-window LEFT TOP WIDTH HEIGHT Sets the position and dimensions of the current window. These are all *pixel* measurememnts. ::end:: */ { VW *vw = curr_vw; DECLARE1(left, NUMBERP); DECLARE2(top, NUMBERP); DECLARE3(width, NUMBERP); DECLARE4(height, NUMBERP); sys_set_vw_pos(vw, VNUM(left), VNUM(top), VNUM(width), VNUM(height)); return(sym_t); } _PR VALUE cmd_current_window(void); DEFUN("current-window", cmd_current_window, subr_current_window, (void), V_Subr0, DOC_current_window) /* ::doc:current_window:: current-window Returns the currently active window. Note that this is the editor's notion of `current' -- it doesn't necessarily mean that this is the window to which your window system will send input events to. ::end:: */ { return(VAL(curr_vw)); } _PR VALUE cmd_with_window(VALUE args); DEFUN("with-window", cmd_with_window, subr_with_window, (VALUE args), V_SF, DOC_with_window) /* ::doc:with_window:: with-window WINDOW FORMS... Set the editor's current window to WINDOW and evaluate FORMS, then reinstall the original window as the current one. ::end:: */ { if(CONSP(args)) { GCVAL gcv_args; VALUE res; PUSHGC(gcv_args, args); if((res = cmd_eval(VCAR(args))) && WINDOWP(res)) { VALUE oldvw = VAL(curr_vw); GCVAL gcv_oldvw; curr_vw = VWIN(res); PUSHGC(gcv_oldvw, oldvw); res = cmd_progn(VCDR(args)); POPGC; curr_vw = VWIN(oldvw); } POPGC; return(res); } return(NULL); } _PR VALUE cmd_set_current_window(VALUE vw, VALUE activ); DEFUN("set-current-window", cmd_set_current_window, subr_set_current_window, (VALUE vw, VALUE activ), V_Subr2, DOC_set_current_window) /* ::doc:set_current_window:: set-current-window WINDOW [ACTIVATE-P] Sets the window which jade reguards as current. If ACTIVATE-P is non-nil the window will be activated with respect to the window-system (under X11 this means warping the pointer to the top left corner of the window as well). ::end:: */ { DECLARE1(vw, WINDOWP); curr_vw = VWIN(vw); if(!NILP(activ)) sys_activate_win(VWIN(vw)); return(VAL(curr_vw)); } _PR VALUE cmd_window_id(VALUE vw); DEFUN("window-id", cmd_window_id, subr_window_id, (VALUE vw), V_Subr1, DOC_window_id) /* ::doc:window_id:: window-id [WINDOW] Returns the identifier of the physical window that the Lisp window WINDOW points to. This is window-system dependant, under X11 it will be some integer, under Intuition a pointer (integer) to the window structure. ::end:: */ { if(!WINDOWP(vw)) vw = VAL(curr_vw); return(make_number((long)VWIN(vw)->vw_Window)); } _PR VALUE cmd_font_x_size(VALUE vw); DEFUN("font-x-size", cmd_font_x_size, subr_font_x_size, (VALUE vw), V_Subr1, DOC_font_x_size) /* ::doc:font_x_size:: font-x-size [WINDOW] Returns the width of the window's font (in pixels). ::end:: */ { if(!WINDOWP(vw)) vw = VAL(curr_vw); return(make_number((long)VWIN(vw)->vw_FontX)); } _PR VALUE cmd_font_y_size(VALUE vw); DEFUN("font-y-size", cmd_font_y_size, subr_font_y_size, (VALUE vw), V_Subr1, DOC_font_x_size) /* ::doc:font_y_size:: font-y-size [WINDOW] Returns the height of the window's font (in pixels). ::end:: */ { if(!WINDOWP(vw)) vw = VAL(curr_vw); return(make_number((long)VWIN(vw)->vw_FontY)); } _PR VALUE var_status_line_cursor(VALUE val); DEFUN("status-line-cursor", var_status_line_cursor, subr_status_line_cursor, (VALUE val), V_Var, DOC_status_line_cursor) /* ::doc:status_line_cursor:: When this window-local variable is non-nil the window's cursor is drawn at the end of the message in the status line, not in the main display. ::end:: */ { if(val) { if(NILP(val)) curr_vw->vw_Flags &= ~VWFF_STATUS_CURS; else curr_vw->vw_Flags |= VWFF_STATUS_CURS; return(val); } return(curr_vw->vw_Flags & VWFF_STATUS_CURS ? sym_t : sym_nil); } _PR VALUE var_buffer_list(VALUE val); DEFUN("buffer-list", var_buffer_list, subr_buffer_list, (VALUE val), V_Var, DOC_buffer_list) /* ::doc:buffer_list:: List of buffers in most-recently-used order. Each window has it's own. ::end:: */ { if(val) curr_vw->vw_BufferList = val; return(curr_vw->vw_BufferList); } void windows_init(void) { ADD_SUBR(subr_make_window); ADD_SUBR(subr_destroy_window); ADD_SUBR(subr_sleep_window); ADD_SUBR(subr_unsleep_window); ADD_SUBR(subr_next_window); ADD_SUBR(subr_message); ADD_SUBR(subr_font_name); ADD_SUBR(subr_max_scroll); ADD_SUBR(subr_y_scroll_step_ratio); ADD_SUBR(subr_x_scroll_step_ratio); ADD_SUBR(subr_rect_blocks_p); ADD_SUBR(subr_set_rect_blocks); ADD_SUBR(subr_window_asleep_p); ADD_SUBR(subr_window_count); ADD_SUBR(subr_position_window); ADD_SUBR(subr_current_window); ADD_SUBR(subr_set_current_window); ADD_SUBR(subr_with_window); ADD_SUBR(subr_window_id); ADD_SUBR(subr_font_x_size); ADD_SUBR(subr_font_y_size); ADD_SUBR(subr_status_line_cursor); ADD_SUBR(subr_buffer_list); INTERN(sym_make_window_hook, "make-window-hook"); INTERN(sym_destroy_window_hook, "destroy-window-hook"); } void windows_kill(void) { VW *vw, *nxt; while(curr_vw) cmd_destroy_window(VAL(curr_vw)); vw = view_chain; while(vw) { nxt = vw->vw_Next; myfree(vw); vw = nxt; } view_chain = NULL; } void window_sweep(void) { VW *vw = view_chain; view_chain = NULL; while(vw) { VW *nxt = vw->vw_Next; if(GC_MARKEDP(VAL(vw))) { GC_CLR(VAL(vw)); vw->vw_Next = view_chain; view_chain = vw; } else myfree(vw); vw = nxt; } } void window_prin(VALUE strm, VALUE vw) { u_char buf[40]; if(VWIN(vw)->vw_Window) { #ifdef HAVE_X11 sprintf(buf, "#<window %ld ", VWIN(vw)->vw_Window); #else sprintf(buf, "#<window 0x%x ", VWIN(vw)->vw_Window); #endif stream_puts(strm, buf, -1, FALSE); stream_puts(strm, VSTR(VWIN(vw)->vw_Tx->tx_BufferName), -1, TRUE); stream_putc(strm, '>'); } else stream_puts(strm, "#<dead-window>", -1, FALSE); }