/* Copyright (C) 2002 Paul Wilkins This program 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 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* lcd.c by Paul Wilkins 3/22/97 */ #include #include #include #include "lcd.h" #include "mode.h" #include "number.h" #include "stack.h" #include "process_input.h" #include "editor.h" #include "error.h" #include "undo.h" #define FONT1 "12x24" #define FONT2 "-*-courier-medium-r-*-*-24-*" #define FONT3 "-misc-fixed-medium-r-normal-*-20-*" #define FONT4 "10x20" #define FONT5 "-*-courier-medium-r-*-*-12-*" #define FONT6 "fixed" /* the amount of spacing we want around the display */ #define BORDER 2 GtkWidget *lcdDA = NULL; char **lcdText = NULL; GdkGC *lcdGC = 0; GdkGC *lcdHighlightGC = 0; /* the last area highlighted */ int dataSelected = 0; int hiX0, hiX1, hiY1; int hiY0, hiX2, hiY2; /* font stuff */ GdkFont *lcdFont; int fontW = 0; int fontH = 0; int fontD = 0; int lcdWidth = 0; int lcdHeight = 0; int lcdDisplayMode = LONG_DISPLAY; int lcdInitalized = 0; /* some callbacks */ static gint lcdExposeCB(GtkWidget *widget, GdkEventExpose *event); static gint lcdResizeCB(GtkWidget *widget, GdkEventConfigure *event); /* local functions */ void calcStackLCD(); void drawStackLCD(); /* this stuff for cut & paste */ #define START_SELECTING 0x1 #define NONE_SELECTED 0x2 #define DATA_SELECTED 0x4 static gint lcdButtonPressEvnt(GtkWidget *widget, GdkEventButton *event); static gint lcdButtonReleaseEvnt(GtkWidget *widget, GdkEventButton *event); static gint lcdMotionEvnt(GtkWidget *widget, GdkEventMotion *event); static gint lcdKeyPressEvnt(GtkWidget *widget, GdkEventKey *event); static gint lcdKeyReleaseEvnt(GtkWidget *widget, GdkEventKey *event); void lcdPasteCB(GtkWidget *, GtkSelectionData *, gpointer); gint loseSelection(GtkWidget *, GdkEventSelection *); #ifdef GTK_VER_1_1 void convertSelection(GtkWidget *, GtkSelectionData *, guint, guint, gpointer); #else void convertSelection(GtkWidget *, GtkSelectionData *, gpointer); #endif void lcdGetSelection(int time); void lcdStartHighlight(int x, int y, int time); void lcdStopHighlight(int x, int y, int time); void lcdContinueHighlight(int x, int y); #define TARGET_STRING 0x1 #define TARGET_TEXT 0x2 #define TARGET_COMPOUND_TEXT 0x4 GtkWidget *setupLCD(GtkWidget *parent, int rows, int cols, char *font){ int i; int width; int wid, hgt; #ifdef GTK_VER_1_1 static GtkTargetEntry targetlist[] = { /* Target Flags Info */ { "STRING", 0, TARGET_STRING }, { "TEXT", 0, TARGET_TEXT }, { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT } }; static gint ntargets = sizeof(targetlist) / sizeof(targetlist[0]); #endif /* store arguments */ lcdWidth = cols; lcdHeight = rows; /* get a font for the main window */ if(font != NULL){ if(NULL == (lcdFont = gdk_font_load(font))){ fprintf(stderr, "Unable to load font %s.\n", font); exit(0); } } else if(NULL == (lcdFont = gdk_font_load(FONT1)) && NULL == (lcdFont = gdk_font_load(FONT2)) && NULL == (lcdFont = gdk_font_load(FONT3)) && NULL == (lcdFont = gdk_font_load(FONT4)) && NULL == (lcdFont = gdk_font_load(FONT5)) && NULL == (lcdFont = gdk_font_load(FONT6))){ fprintf(stderr, "Unable to load a font.\n"); exit(0); } /* find max font width */ for(i=0; i<256; i++){ width = gdk_char_width(lcdFont, (gchar)i); if(width < 50 && width > fontW) fontW = width; } /* globals we use all over the place */ fontH = lcdFont->ascent + lcdFont->descent; fontD = lcdFont->descent; if(fontW == 0 || fontH == 0){ fprintf(stderr, "Error: can not determine font dimentions.\n"); exit(0); } wid = (2 * BORDER) + (lcdWidth * fontW); hgt = (2 * BORDER) + (lcdHeight * fontH); lcdDA = gtk_drawing_area_new(); gtk_drawing_area_size(GTK_DRAWING_AREA(lcdDA), wid, hgt); gtk_box_pack_start(GTK_BOX(parent), lcdDA, TRUE, TRUE, 0); gtk_widget_show(lcdDA); /* Signals used to handle window ops */ gtk_signal_connect(GTK_OBJECT(lcdDA), "expose_event", (GtkSignalFunc)lcdExposeCB, NULL); gtk_signal_connect(GTK_OBJECT(lcdDA),"configure_event", (GtkSignalFunc)lcdResizeCB, NULL); /* Event signals (Selection) */ gtk_signal_connect(GTK_OBJECT(lcdDA), "selection_received", (GtkSignalFunc)lcdPasteCB, NULL); gtk_signal_connect(GTK_OBJECT(lcdDA), "selection_clear_event", (GtkSignalFunc)loseSelection, NULL); /* gtk_signal_connect(GTK_OBJECT(lcdDA), "selection_request_event", (GtkSignalFunc)convertSelection, NULL); */ #ifdef GTK_VER_1_1 gtk_selection_add_targets(lcdDA, GDK_SELECTION_PRIMARY, targetlist, ntargets); gtk_signal_connect(GTK_OBJECT(lcdDA), "selection_get", (GtkSignalFunc)convertSelection, NULL); #else gtk_selection_add_handler(lcdDA, GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, convertSelection, NULL); #endif /* Event signals (Input) */ gtk_signal_connect(GTK_OBJECT(lcdDA), "motion_notify_event", (GtkSignalFunc)lcdMotionEvnt, NULL); gtk_signal_connect(GTK_OBJECT(lcdDA), "button_press_event", (GtkSignalFunc)lcdButtonPressEvnt, NULL); gtk_signal_connect(GTK_OBJECT(lcdDA), "button_release_event", (GtkSignalFunc)lcdButtonReleaseEvnt, NULL); gtk_signal_connect_after(GTK_OBJECT(lcdDA), "key_press_event", (GtkSignalFunc)lcdKeyPressEvnt, NULL); gtk_signal_connect_after(GTK_OBJECT(lcdDA), "key_release_event", (GtkSignalFunc)lcdKeyReleaseEvnt, NULL); gtk_widget_set_events(lcdDA, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); GTK_WIDGET_SET_FLAGS(lcdDA, GTK_CAN_FOCUS); gtk_widget_grab_focus(lcdDA); return lcdDA; } void clearLCD(){ /* clear the stack */ clearStack(); /* reset any error string */ resetError(); /* clear the entry buffer */ cancelEditor(); /* re-initialize (and clear) the undo system */ initUndoSystem(); /* redraw the window */ calcStackLCD(); drawStackLCD(); } void clearLCDwindow(){ gdk_window_clear(lcdDA->window); } /* Recalculate the contents of the lcd display. * note: we don't redraw the lcd here, that is done by drawStackLCD(). */ void calcStackLCD(){ int i, j; int strLen, pLen, curPos; char *c, *p; char *txt; /* the number */ char label[16]; /* the stack number label */ int height; int indx; int row; /* the row to start print the current number */ int txtPos; /* which col do we start printing in */ int lines; /* the number of lines this number has */ int top; /* which row is the top for drawing the stack */ int bottom; /* which row is the current bottom for drawing the stack */ int labelLen; /* the stack number label length */ Number *num; if(isEditingEditor()){ height = lcdHeight - 1; } else { height = lcdHeight; } if(height <= 0) return; /* clear the old display */ for(i=0; i lcdWidth) strLen = lcdWidth; strncpy(lcdText[0], c, strLen); } else { top = 0; } bottom = height; /* print all of the numbers */ for(indx=0; indx lcdWidth) labelLen = lcdWidth; strncpy(lcdText[row], label, labelLen); /* print the number */ p = txt; for(j=0, i=row; i lcdWidth) break; strncpy(&(lcdText[i][txtPos]), p, lcdWidth-txtPos); p = p + pLen + 1; } /* free the mem used to print the number */ free(txt); bottom = bottom - j; if(bottom == top) break; } /* fill in the rest of the indexes */ for(i=indx, j=bottom; j>top; i++, j--){ sprintf(label, "%d: ", i+1); labelLen = strlen(label); if(labelLen > lcdWidth) labelLen = lcdWidth; strncpy(lcdText[j-1], label, labelLen); } if(isEditingEditor()){ txt = getLineEditor(); curPos = cursorPosEditor(); if(curPos > lcdWidth){ txt += (curPos - lcdWidth); } strLen = strlen(txt); if(strLen > lcdWidth) strLen = lcdWidth; strncpy(lcdText[lcdHeight-1], txt, strLen); } } /* Redraw the contents of the lcd display. * note: start BORDER pixels down and to the right. * TODO: we could be smart and only redraw what has changed. * This does this work correctly with varriable sized fonts. */ void drawStackLCD(){ int i; int curPos; int strt, stop; GdkGC *drawgc; /* draw the stack */ for(i=0; i=hiY1 && i<=hiY2){ stop = hiX1; if(stop > lcdWidth) stop = lcdWidth; } else { stop = lcdWidth; } if(stop-strt){ gdk_window_clear_area(lcdDA->window, strt*fontW + BORDER, i*fontH + BORDER, fontW*(stop-strt), fontH); gdk_draw_text(lcdDA->window, lcdFont, lcdGC, strt*fontW + BORDER, (i+1)*fontH - fontD + BORDER, &lcdText[i][strt], stop-strt); } /* draw the highlighted section of the line */ if(dataSelected == DATA_SELECTED && i>=hiY1 && i<=hiY2){ strt = hiX1; if(strt > lcdWidth) strt = lcdWidth; stop = hiX2+1; if(stop > lcdWidth) stop = lcdWidth; if(stop-strt){ gdk_draw_rectangle(lcdDA->window, lcdGC, TRUE, strt*fontW + BORDER, i*fontH + BORDER, fontW*(stop-strt), fontH); gdk_draw_text(lcdDA->window, lcdFont, lcdHighlightGC, strt*fontW + BORDER, (i+1)*fontH - fontD + BORDER, &lcdText[i][strt], stop-strt); } } /* draw the last unhighlighted section of the line */ stop = lcdWidth; if(dataSelected == DATA_SELECTED && i>=hiY1 && i<=hiY2){ strt = hiX2+1; if(strt > lcdWidth) strt = lcdWidth; } else { strt = lcdWidth; } if(stop-strt){ gdk_window_clear_area(lcdDA->window, strt*fontW + BORDER, i*fontH + BORDER, fontW*(stop-strt), fontH); gdk_draw_text(lcdDA->window, lcdFont, lcdGC, strt*fontW + BORDER, (i+1)*fontH - fontD + BORDER, &lcdText[i][strt], stop-strt); } } /* draw the cursor */ if(isEditingEditor()){ curPos = cursorPosEditor(); if(curPos > lcdWidth){ curPos = lcdWidth; } if(dataSelected == DATA_SELECTED && curPos>=hiX1 && curPos<=hiX2 && lcdHeight-1>=hiY1 && lcdHeight-1<=hiY2) { drawgc = lcdHighlightGC; } else { drawgc = lcdGC; } /* hack: draw a white line to erase any old cursor in position 0. XDrawImageString dosen't cover this up like it does with an old cursor in any other position */ if(!(dataSelected == DATA_SELECTED && 0==hiX1 && lcdHeight-1>=hiY1 && lcdHeight-1<=hiY2)) { gdk_draw_line(lcdDA->window, lcdHighlightGC, BORDER - 1, (lcdHeight-1) * fontH + BORDER, BORDER - 1, lcdHeight * fontH - 1 + BORDER); } /* draw the cursor */ gdk_draw_line(lcdDA->window, drawgc, fontW * curPos - 1 + BORDER, (lcdHeight-1) * fontH + BORDER, fontW * curPos - 1 + BORDER, lcdHeight * fontH - 1 + BORDER); } } /* redraw what we need to */ void redrawLCD(){ calcStackLCD(); drawStackLCD(); } void lcdResize(){ int i; char *buf; int width, height; width = lcdDA->allocation.width; height = lcdDA->allocation.height; lcdWidth = (width - 2*BORDER) / fontW; if(lcdWidth < 0) lcdWidth = 0; lcdHeight = (height - 2*BORDER) / fontH; if(lcdHeight < 0) lcdHeight = 0; /* free the old mem */ if(lcdText){ free(lcdText[0]); free(lcdText); } /* get the new mem. note: add 1 bec size could otherwize be 0. */ if(NULL == (lcdText=(char**)malloc((1+lcdHeight)*sizeof(char *)))){ perror("malloc"); exit(0); } if(NULL == (buf=(char*)malloc(1+lcdHeight*lcdWidth*sizeof(char)))){ perror("malloc"); exit(0); } lcdText[0] = buf; /* in case lcdHeight == 0 */ for(i=0; ifg_gc[GTK_STATE_NORMAL]; lcdHighlightGC = style->bg_gc[GTK_STATE_NORMAL]; lcdResize(); lcdInitalized = 1; } clearLCDwindow(); drawStackLCD(); return FALSE; } static gint lcdResizeCB(GtkWidget *widget, GdkEventConfigure *event){ if(lcdInitalized != 0) lcdResize(); return TRUE; } static gint lcdKeyPressEvnt(GtkWidget *widget, GdkEventKey *event){ char c; if(event->string) c = *(event->string); else c = 'd'; /* just pick something */ processInput( event->keyval, event->state & GDK_CONTROL_MASK ? 1 : 0, c, 'd'); return TRUE; } static gint lcdKeyReleaseEvnt(GtkWidget *widget, GdkEventKey *event){ return TRUE; } static gint lcdButtonPressEvnt(GtkWidget *widget, GdkEventButton *event) { gtk_widget_grab_focus(lcdDA); if(event->button == 1){ lcdStartHighlight(event->x, event->y, event->time); } } static gint lcdButtonReleaseEvnt(GtkWidget *widget, GdkEventButton *event) { switch(event->button){ case 1: /* stop highlighting an area */ lcdStopHighlight(event->x, event->y, event->time); break; case 2: /* paste data */ lcdGetSelection(event->time); break; case 3: /* might be used to continue highlighting */ if(dataSelected == DATA_SELECTED){ lcdContinueHighlight(event->x, event->y); lcdStopHighlight(event->x, event->y, event->time); } break; } } static gint lcdMotionEvnt(GtkWidget *widget, GdkEventMotion *event) { int x, y; GdkModifierType state; if(event->is_hint){ gdk_window_get_pointer(event->window, &x, &y, &state); } else { x = event->x; y = event->y; state = event->state; } if(state & GDK_BUTTON1_MASK){ /* highlighting with button 1 */ lcdContinueHighlight(x, y); } else if(state & GDK_BUTTON3_MASK && dataSelected == DATA_SELECTED){ /* continuing a highlight with button 3 */ lcdContinueHighlight(x, y); } return TRUE; } #ifdef GTK_VER_1_1 void convertSelection( GtkWidget *widget, GtkSelectionData *selection, guint info, guint time, gpointer data) #else void convertSelection( GtkWidget *widget, GtkSelectionData *selection, gpointer data) #endif { int i; int width, height; char *str; guchar *text; gint len; gint format; GdkAtom type; /* no highlight */ if(dataSelected != DATA_SELECTED){ printf("convertSelection called, but no selection.\n"); } else { width = hiX2 - hiX1 + 1; height = hiY2 - hiY1 + 1; len = (width + 1) * height; str = (char *)malloc(len * sizeof(char)); for(i=0; itarget){ #endif case GDK_TARGET_STRING: case TARGET_STRING: gtk_selection_data_set( selection, GDK_SELECTION_TYPE_STRING, 8, str, len-1); break; case TARGET_TEXT: case TARGET_COMPOUND_TEXT: gdk_string_to_compound_text(str, &type, &format, &text, &len); gtk_selection_data_set( selection, type, format, text, len); gdk_free_compound_text(text); break; } } } gint loseSelection( GtkWidget *widget, GdkEventSelection *event) { /* if there is a current (old) selection, then clear it */ if(dataSelected == DATA_SELECTED){ dataSelected = NONE_SELECTED; drawStackLCD(); } return TRUE; } void lcdStartHighlight(int x, int y, int time){ int xx, yy; /* if there is a current (old) selection, then clear it */ if(dataSelected == DATA_SELECTED){ dataSelected = NONE_SELECTED; drawStackLCD(); /* ... and tell GDK */ if(gdk_selection_owner_get(GDK_SELECTION_PRIMARY) == lcdDA->window){ gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME); } } xx = (x - BORDER) / fontW; if(xx < 0) xx = 0; if(xx >= lcdWidth) xx = lcdWidth - 1; yy = (y - BORDER) / fontH; if(yy < 0) yy = 0; if(yy >= lcdHeight) yy = lcdHeight - 1; hiX0 = hiX1 = hiX2 = xx; hiY0 = hiY1 = hiY2 = yy; dataSelected = START_SELECTING; } void lcdStopHighlight(int x, int y, int time){ int reCalc, curPos; int xx, yy; /* if the user started to select something, but didn't actually select anything, then reset stuff */ if(dataSelected == START_SELECTING){ dataSelected = NONE_SELECTED; /* user may have been positioning the edit cursor */ if(isEditingEditor()){ xx = (x - BORDER) / fontW; if(xx < 0) xx = 0; if(xx >= lcdWidth) xx = lcdWidth - 1; yy = (y - BORDER) / fontH; if(yy == lcdHeight-1){ curPos = cursorPosEditor(); if(curPos > lcdWidth){ reCalc = 1; xx += (curPos - lcdWidth); } else { reCalc = 0; } setCursorPosEditor(xx); /* need to recalc since cursor may have moved out of the visable part of the editor */ if(reCalc) calcStackLCD(); } } /* clear any old highlights */ drawStackLCD(); } /* if data is now selected */ if(dataSelected == DATA_SELECTED){ if(0 == gtk_selection_owner_set( lcdDA, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME)) { fprintf(stderr, "failed to gain selection\n"); dataSelected = NONE_SELECTED; drawStackLCD(); } } } void lcdContinueHighlight(int x, int y){ int xx, yy; int lastx1, lastx2, lasty1, lasty2; xx = (x - BORDER) / fontW; if(xx < 0) xx = 0; if(xx >= lcdWidth) xx = lcdWidth - 1; yy = (y - BORDER) / fontH; if(yy < 0) yy = 0; if(yy >= lcdHeight) yy = lcdHeight - 1; lastx1 = hiX1; lastx2 = hiX2; lasty1 = hiY1; lasty2 = hiY2; if(xx < hiX0){ hiX1 = xx; hiX2 = hiX0 + 0; } else { hiX1 = hiX0; hiX2 = xx + 0; } if(yy < hiY0){ hiY1 = yy; hiY2 = hiY0 + 0; } else { hiY1 = hiY0; hiY2 = yy + 0; } /* if the highlight area changed */ if(lastx1 != hiX1 || lastx2 != hiX2 || lasty1 != hiY1 || lasty2 != hiY2){ dataSelected = DATA_SELECTED; /* repaint the display */ drawStackLCD(); } } /* ask for data */ void lcdGetSelection(int time){ /* And request the primary selection */ gtk_selection_convert(lcdDA, GDK_SELECTION_PRIMARY, /* GdkAtom selection */ GDK_TARGET_STRING, /* GdkAtom target */ GDK_CURRENT_TIME); /* guint32 time */ } void lcdPasteCB( GtkWidget *widget, GtkSelectionData *selection, gpointer data) { int i; char *str; static GdkAtom clipboard_atom = GDK_NONE; if(selection->length <= 0 || selection->type != GDK_SELECTION_TYPE_STRING){ /* printf("length is zero or did not get a string.\n"); */ if(selection->selection != GDK_SELECTION_PRIMARY) return; /* selection failed. Try the clipboard now... */ if(clipboard_atom == GDK_NONE){ clipboard_atom = gdk_atom_intern("CLIPBOARD", FALSE); } /* ask for another selection, but this time from the clipboard */ gtk_selection_convert(lcdDA, clipboard_atom, /* GdkAtom selection */ GDK_TARGET_STRING, /* GdkAtom target */ GDK_CURRENT_TIME); /* guint32 time */ return; } str = (char*)selection->data; for(i=0; ilength; i++){ /* TODO maybe do something like isalpha() here? */ processInput(0, 0, str[i], str[i]); } }