diff options
Diffstat (limited to 'ext')
52 files changed, 8369 insertions, 819 deletions
@@ -10,3 +10,4 @@ #socket #tkutil #tcltklib +#gtk diff --git a/ext/Win32API/MANIFEST b/ext/Win32API/MANIFEST new file mode 100644 index 0000000000..7cc9ac445e --- /dev/null +++ b/ext/Win32API/MANIFEST @@ -0,0 +1,7 @@ +MANIFEST +depend +MANIFEST +Win32API.c +extconf.rb +getch.rb +point.rb diff --git a/ext/Win32API/Win32API.c b/ext/Win32API/Win32API.c new file mode 100644 index 0000000000..f4cb5c726f --- /dev/null +++ b/ext/Win32API/Win32API.c @@ -0,0 +1,223 @@ +/* + Win32API - Ruby Win32 API Import Facility +*/ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdio.h> + +#define _T_VOID 0 +#define _T_NUMBER 1 +#define _T_POINTER 2 +#define _T_INTEGER 3 + +typedef char *ApiPointer(void); +typedef long ApiNumber(void); +typedef void ApiVoid(void); +typedef int ApiInteger(void); + +#include "ruby.h" + +typedef struct { + HANDLE dll; + HANDLE proc; + VALUE dllname; + VALUE import; + VALUE export; +} Win32API; + +static void +Win32API_FreeLibrary(hdll) + HINSTANCE hdll; +{ + FreeLibrary(hdll); +} + +static VALUE +Win32API_initialize(self, dllname, proc, import, export) + VALUE self; + VALUE dllname; + VALUE proc; + VALUE import; + VALUE export; +{ + HANDLE hproc; + HINSTANCE hdll; + VALUE str; + VALUE a_import; + VALUE *ptr; + int i; + int len; + int ex; + + hdll = GetModuleHandle(RSTRING(dllname)->ptr); + if (!hdll) { + hdll = LoadLibrary(RSTRING(dllname)->ptr); + if (!hdll) + Fail("LoadLibrary: %s\n", RSTRING(dllname)->ptr); + Data_Wrap_Struct(self, 0, Win32API_FreeLibrary, hdll); + } + hproc = GetProcAddress(hdll, RSTRING(proc)->ptr); + if (!hproc) { + str = str_new3(proc); + str = str_cat(str, "A", 1); + hproc = GetProcAddress(hdll, RSTRING(str)->ptr); + if (!hproc) + Fail("GetProcAddress: %s or %s\n", + RSTRING(proc)->ptr, RSTRING(str)->ptr); + } + rb_iv_set(self, "__dll__", INT2NUM((int)hdll)); + rb_iv_set(self, "__dllname__", dllname); + rb_iv_set(self, "__proc__", INT2NUM((int)hproc)); + + a_import = ary_new(); + ptr = RARRAY(import)->ptr; + for (i = 0, len = RARRAY(import)->len; i < len; i++) { + int c = *(char *)RSTRING(ptr[i])->ptr; + switch (c) { + case 'N': case 'n': case 'L': case 'l': + ary_push(a_import, INT2FIX(_T_NUMBER)); + break; + case 'P': case 'p': + ary_push(a_import, INT2FIX(_T_POINTER)); + break; + case 'I': case 'i': + ary_push(a_import, INT2FIX(_T_INTEGER)); + break; + } + } + rb_iv_set(self, "__import__", a_import); + + switch (*RSTRING(export)->ptr) { + case 'V': case 'v': + ex = _T_VOID; + break; + case 'N': case 'n': case 'L': case 'l': + ex = _T_NUMBER; + break; + case 'P': case 'p': + ex = _T_POINTER; + break; + case 'I': case 'i': + ex = _T_INTEGER; + break; + } + rb_iv_set(self, "__export__", INT2FIX(ex)); + + return Qnil; +} + +static VALUE +Win32API_Call(argc, argv, obj) + VALUE argc; + VALUE argv; + VALUE obj; +{ + VALUE args; + + FARPROC ApiFunction; + + ApiPointer *ApiFunctionPointer; + ApiNumber *ApiFunctionNumber; + ApiVoid *ApiFunctionVoid; + ApiInteger *ApiFunctionInteger; + + long lParam; + char *pParam; + + VALUE Return; + + VALUE obj_proc; + VALUE obj_import; + VALUE obj_export; + VALUE import_type; + int nimport, timport, texport, i; + int items; + + items = rb_scan_args(argc, argv, "0*", &args); + + obj_proc = rb_iv_get(obj, "__proc__"); + + ApiFunction = (FARPROC)NUM2INT(obj_proc); + + obj_import = rb_iv_get(obj, "__import__"); + obj_export = rb_iv_get(obj, "__export__"); + nimport = RARRAY(obj_import)->len; + texport = FIX2INT(obj_export); + + if (items != nimport) + Fail("Wrong number of parameters: expected %d, got %d.\n", + nimport, items); + + if (0 < nimport) { + for (i = nimport - 1; 0 <= i; i--) { + VALUE str; + import_type = ary_entry(obj_import, i); + timport = FIX2INT(import_type); + switch (timport) { + case _T_NUMBER: + case _T_INTEGER: + lParam = NUM2INT(ary_entry(args, i)); +#if defined(_MSC_VER) || defined(__LCC__) + _asm { + mov eax, lParam + push eax + } +#elif defined(__CYGWIN32__) || defined(__MINGW32__) + asm volatile ("pushl %0" :: "g" (lParam)); +#else +#error +#endif + break; + case _T_POINTER: + str = ary_entry(args, i); + Check_Type(str, T_STRING); + str_modify(str); + pParam = RSTRING(str)->ptr; +#if defined(_MSC_VER) || defined(__LCC__) + _asm { + mov eax, dword ptr pParam + push eax + } +#elif defined(__CYGWIN32__) || defined(__MINGW32__) + asm volatile ("pushl %0" :: "g" (pParam)); +#else +#error +#endif + break; + } + } + + } + + switch (texport) { + case _T_NUMBER: + ApiFunctionNumber = (ApiNumber *) ApiFunction; + Return = INT2NUM(ApiFunctionNumber()); + break; + case _T_POINTER: + ApiFunctionPointer = (ApiPointer *) ApiFunction; + Return = str_new2((char *)ApiFunctionPointer()); + break; + case _T_INTEGER: + ApiFunctionInteger = (ApiInteger *) ApiFunction; + Return = INT2NUM(ApiFunctionInteger()); + break; + case _T_VOID: + default: + ApiFunctionVoid = (ApiVoid *) ApiFunction; + ApiFunctionVoid(); + Return = INT2NUM(0); + break; + } + return Return; +} + +void +Init_Win32API() +{ + VALUE cWin32API = rb_define_class("Win32API", cObject); + rb_define_method(cWin32API, "initialize", Win32API_initialize, 4); + rb_define_method(cWin32API, "call", Win32API_Call, -1); + rb_define_alias(cWin32API, "Call", "call"); +} diff --git a/ext/Win32API/extconf.rb b/ext/Win32API/extconf.rb new file mode 100644 index 0000000000..5af3243d96 --- /dev/null +++ b/ext/Win32API/extconf.rb @@ -0,0 +1,7 @@ +case PLATFORM +when /cygwin/,/mingw/ + $CFLAGS = "-fno-defer-pop" + create_makefile("Win32API") +when /win32/ + create_makefile("Win32API") +end diff --git a/ext/Win32API/getch.rb b/ext/Win32API/getch.rb new file mode 100644 index 0000000000..c015bbe9bc --- /dev/null +++ b/ext/Win32API/getch.rb @@ -0,0 +1,5 @@ +require 'Win32API' + +getch = Win32API.new("crtdll", "_getch", [], 'L') + +puts getch.Call.chr diff --git a/ext/Win32API/point.rb b/ext/Win32API/point.rb new file mode 100644 index 0000000000..60e265f3ee --- /dev/null +++ b/ext/Win32API/point.rb @@ -0,0 +1,18 @@ +require 'Win32API' + +getCursorPos = Win32API.new("user32", "GetCursorPos", ['P'], 'V') + +lpPoint = " " * 8 # store two LONGs +getCursorPos.Call(lpPoint) +x, y = lpPoint.unpack("LL") # get the actual values + +print "x: ", x, "\n" +print "y: ", y, "\n" + +ods = Win32API.new("kernel32", "OutputDebugString", ['P'], 'V') +ods.Call("Hello, World\n"); + +GetDesktopWindow = Win32API.new("user32", "GetDesktopWindow", [], 'L') +GetActiveWindow = Win32API.new("user32", "GetActiveWindow", [], 'L') +SendMessage = Win32API.new("user32", "SendMessage", ['L'] * 4, 'L') +SendMessage.Call GetDesktopWindow.Call, 274, 0xf140, 0 diff --git a/ext/aix_ld.rb b/ext/aix_ld.rb index 1058977b88..42b2087a46 100644 --- a/ext/aix_ld.rb +++ b/ext/aix_ld.rb @@ -43,7 +43,7 @@ def extract(nm, out) else next end - }.sort! + }.compact!.sort! uniq(data) exp = open(out, "w") for line in data diff --git a/ext/curses/curses.c b/ext/curses/curses.c index 3ae8db192d..f3d1bc0970 100644 --- a/ext/curses/curses.c +++ b/ext/curses/curses.c @@ -2,7 +2,7 @@ * ext/curses/curses.c * * by MAEDA Shugo (ender@pic-internet.or.jp) - * modified by Yukihiro Matsumoto (matz@ruby.club.or.jp) + * modified by Yukihiro Matsumoto (matz@netlab.co.jp) */ #ifdef HAVE_NCURSES_H @@ -12,12 +12,18 @@ # include <ncurses/curses.h> # else # include <curses.h> -# if defined(__NetBSD__) && !defined(_maxx) +# if (defined(__bsdi__) || defined(__NetBSD__)) && !defined(_maxx) # define _maxx maxx # endif -# if defined(__NetBSD__) && !defined(_maxy) +# if (defined(__bsdi__) || defined(__NetBSD__)) && !defined(_maxy) # define _maxy maxy # endif +# if (defined(__bsdi__) || defined(__NetBSD__)) && !defined(_begx) +# define _begx begx +# endif +# if (defined(__bsdi__) || defined(__NetBSD__)) && !defined(_begy) +# define _begy begy +# endif # endif #endif @@ -32,14 +38,10 @@ struct windata { WINDOW *window; }; -#define NUM2CHAR(x) ((TYPE(x) == T_STRING)&&(RSTRING(x)->len>=1))?\ - RSTRING(x)->ptr[0]:(char)NUM2INT(x) -#define CHAR2FIX(x) INT2FIX((int)x) - static void no_window() { - Fail("already closed window"); + rb_raise(rb_eRuntimeError, "already closed window"); } #define GetWINDOW(obj, winp) {\ @@ -55,6 +57,7 @@ free_window(winp) { if (winp->window && winp->window != stdscr) delwin(winp->window); winp->window = 0; + free(winp); } static VALUE @@ -66,7 +69,7 @@ prep_window(class, window) struct windata *winp; if (window == NULL) { - Fail("failed to create window"); + rb_raise(rb_eRuntimeError, "failed to create window"); } obj = Data_Make_Struct(class, struct windata, 0, free_window, winp); @@ -83,7 +86,7 @@ curses_init_screen() { initscr(); if (stdscr == 0) { - Fail("cannot initialize curses"); + rb_raise(rb_eRuntimeError, "cannot initialize curses"); } clear(); rb_stdscr = prep_window(cWindow, stdscr); @@ -102,19 +105,33 @@ curses_stdscr() static VALUE curses_close_screen() { - endwin(); +#ifdef HAVE_ISENDWIN + if (!isendwin()) +#endif + endwin(); return Qnil; } +static void +curses_finalize() +{ + if (stdscr +#ifdef HAVE_ISENDWIN + && !isendwin() +#endif + ) + endwin(); +} + /* def closed? */ static VALUE curses_closed() { -#ifdef HAVE_ENDWIN +#ifdef HAVE_ISENDWIN if (isendwin()) { - return TRUE; + return Qtrue; } - return FALSE; + return Qfalse; #else rb_notimplement(); #endif @@ -138,12 +155,16 @@ curses_refresh(obj) return Qnil; } -/* def refresh */ +/* def doupdate */ static VALUE curses_doupdate(obj) VALUE obj; { +#ifdef HAVE_DOUPDATE doupdate(); +#else + refresh(); +#endif return Qnil; } @@ -235,7 +256,9 @@ static VALUE curses_flash(obj) VALUE obj; { +#ifdef HAVE_FLASH flash(); +#endif return Qnil; } @@ -287,7 +310,7 @@ static VALUE curses_inch(obj) VALUE obj; { - return CHAR2FIX(inch()); + return CHR2FIX(inch()); } /* def addch(ch) */ @@ -296,7 +319,7 @@ curses_addch(obj, ch) VALUE obj; VALUE ch; { - addch(NUM2CHAR(ch)); + addch(NUM2CHR(ch)); return Qnil; } @@ -306,7 +329,7 @@ curses_insch(obj, ch) VALUE obj; VALUE ch; { - insch(NUM2CHAR(ch)); + insch(NUM2CHR(ch)); return Qnil; } @@ -316,7 +339,9 @@ curses_addstr(obj, str) VALUE obj; VALUE str; { - addstr(RSTRING(str)->ptr); + if (!NIL_P(str)) { + addstr(STR2CSTR(str)); + } return Qnil; } @@ -325,7 +350,7 @@ static VALUE curses_getch(obj) VALUE obj; { - return CHAR2FIX(getch()); + return CHR2FIX(getch()); } /* def getstr */ @@ -335,7 +360,7 @@ curses_getstr(obj) { char rtn[1024]; /* This should be big enough.. I hope */ getstr(rtn); - return str_taint(str_new2(rtn)); + return rb_tainted_str_new2(rtn); } /* def delch */ @@ -352,7 +377,9 @@ static VALUE curses_deleteln(obj) VALUE obj; { +#ifdef HAVE_DELETELN deleteln(); +#endif return Qnil; } @@ -379,11 +406,15 @@ window_s_new(class, lines, cols, top, left) VALUE top; VALUE left; { + VALUE w; WINDOW *window; window = newwin(NUM2INT(lines), NUM2INT(cols), NUM2INT(top), NUM2INT(left)); wclear(window); - return prep_window(class, window); + w = prep_window(class, window); + rb_obj_call_init(w); + + return w; } /* def subwin(lines, cols, top, left) */ @@ -412,7 +443,8 @@ window_close(obj) struct windata *winp; GetWINDOW(obj, winp); - free_window(winp); + delwin(winp->window); + winp->window = 0; return Qnil; } @@ -453,7 +485,7 @@ window_box(obj, vert, hor) struct windata *winp; GetWINDOW(obj, winp); - box(winp->window, NUM2CHAR(vert), NUM2CHAR(hor)); + box(winp->window, NUM2CHR(vert), NUM2CHR(hor)); return Qnil; } @@ -622,7 +654,7 @@ window_inch(obj) struct windata *winp; GetWINDOW(obj, winp); - return CHAR2FIX(winch(winp->window)); + return CHR2FIX(winch(winp->window)); } /* def addch(ch) */ @@ -634,7 +666,7 @@ window_addch(obj, ch) struct windata *winp; GetWINDOW(obj, winp); - waddch(winp->window, NUM2CHAR(ch)); + waddch(winp->window, NUM2CHR(ch)); return Qnil; } @@ -648,7 +680,7 @@ window_insch(obj, ch) struct windata *winp; GetWINDOW(obj, winp); - winsch(winp->window, NUM2CHAR(ch)); + winsch(winp->window, NUM2CHR(ch)); return Qnil; } @@ -659,11 +691,12 @@ window_addstr(obj, str) VALUE obj; VALUE str; { - struct windata *winp; - - GetWINDOW(obj, winp); - waddstr(winp->window, RSTRING(str)->ptr); - + if (!NIL_P(str)) { + struct windata *winp; + + GetWINDOW(obj, winp); + waddstr(winp->window, STR2CSTR(str)); + } return Qnil; } @@ -685,7 +718,7 @@ window_getch(obj) struct windata *winp; GetWINDOW(obj, winp); - return CHAR2FIX(wgetch(winp->window)); + return CHR2FIX(wgetch(winp->window)); } /* def getstr */ @@ -698,7 +731,7 @@ window_getstr(obj) GetWINDOW(obj, winp); wgetstr(winp->window, rtn); - return str_taint(str_new2(rtn)); + return rb_tainted_str_new2(rtn); } /* def delch */ @@ -718,10 +751,12 @@ static VALUE window_deleteln(obj) VALUE obj; { +#ifdef HAVE_WDELETELN struct windata *winp; GetWINDOW(obj, winp); wdeleteln(winp->window); +#endif return Qnil; } @@ -764,7 +799,7 @@ Init_curses() rb_define_module_function(mCurses, "lines", curses_lines, 0); rb_define_module_function(mCurses, "cols", curses_cols, 0); - cWindow = rb_define_class_under(mCurses, "Window", cObject); + cWindow = rb_define_class_under(mCurses, "Window", rb_cObject); rb_define_singleton_method(cWindow, "new", window_s_new, 4); rb_define_method(cWindow, "subwin", window_subwin, 4); rb_define_method(cWindow, "close", window_close, 0); @@ -790,4 +825,6 @@ Init_curses() rb_define_method(cWindow, "getstr", window_getstr, 0); rb_define_method(cWindow, "delch", window_delch, 0); rb_define_method(cWindow, "deleteln", window_deleteln, 0); + + rb_set_end_proc(curses_finalize, 0); } diff --git a/ext/curses/extconf.rb b/ext/curses/extconf.rb index 9b28437843..442a9424a2 100644 --- a/ext/curses/extconf.rb +++ b/ext/curses/extconf.rb @@ -1,6 +1,9 @@ +require 'mkmf' $CFLAGS="-I/usr/include/ncurses -I/usr/local/include/ncurses" $LDFLAGS="-L/usr/local/lib" make=FALSE + +have_library("mytinfo", "tgetent") if /bow/ =~ PLATFORM if have_header("ncurses.h") and have_library("ncurses", "initscr") make=TRUE elsif have_header("ncurses/curses.h") and have_library("ncurses", "initscr") @@ -14,7 +17,7 @@ else end if make then - for f in ["isendwin", "ungetch", "beep"] + for f in %w(isendwin ungetch beep doupdate flash deleteln wdeleteln) have_func(f) end create_makefile("curses") diff --git a/ext/curses/hello.rb b/ext/curses/hello.rb index bed7779aac..a1915ce60d 100644 --- a/ext/curses/hello.rb +++ b/ext/curses/hello.rb @@ -10,6 +10,7 @@ def show_message(message) win.box(?|, ?=) win.setpos(2, 3) win.addstr(message) + win.refresh win.getch win.close end @@ -18,8 +19,9 @@ init_screen begin crmode # show_message("Hit any key") - setpos (lines - 5) / 2, (cols - 10) / 2 + setpos((lines - 5) / 2, (cols - 10) / 2) addstr("Hit any key") + refresh getch show_message("Hello, World!") refresh diff --git a/ext/curses/view.rb b/ext/curses/view.rb index e59a74ed44..5ba1a8413c 100644 --- a/ext/curses/view.rb +++ b/ext/curses/view.rb @@ -43,6 +43,7 @@ while TRUE addstr(data_lines[lptr + i]) #if data_lines[lptr + i] i += 1 end + refresh explicit = FALSE n = 0 diff --git a/ext/cygwin32_ld.rb b/ext/cygwin32_ld.rb new file mode 100644 index 0000000000..a9c8e21cb0 --- /dev/null +++ b/ext/cygwin32_ld.rb @@ -0,0 +1,90 @@ +#!/usr/local/bin/ruby +require '../../rbconfig' +include Config + +args = ARGV.join(" ") + +objs = [] +flags = [] +libname = '' +Init = "../init" + +path = '' + +def writeInit + out = open("#{Init}.c", "w") + + out.print %q@ +#include <windows.h> +#include <stdio.h> + +extern struct _reent *__imp_reent_data; +WINAPI dll_entry(int a, int b, int c) +{ + _impure_ptr =__imp_reent_data; + return 1; +} +main(){} +//void impure_setup(struct _reent *_impure_ptrMain) +//{ +// _impure_ptr =__imp_reent_data; +//} +@ + out.close +end + +def xsystem cmd + print cmd, "\n" + system cmd +end + +if args =~ /-o (\w+)\.dll/i + libname = $1 + # Check for path: + if libname =~ /(\w+\/)(\w+)$/ + path = $1 + libname = $2 + end + for arg in ARGV + case arg + when /\.[oa]$/i + objs.push(arg) + when /-o/, /\w+\.dll/i + ; + else + flags << arg + end + end + + writeInit unless FileTest.exist?("#{Init}.c") + unless FileTest.exist?("#{Init}.o") and + File.mtime("#{Init}.c") < File.mtime("#{Init}.o") + xsystem "gcc -c #{Init}.c -o #{Init}.o" + end + + command = "echo EXPORTS > #{libname}.def" + xsystem command +# xsystem "echo impure_setup >> #{libname}.def" + xsystem "nm --extern-only " + objs.join(" ") + + " | sed -n '/^........ [CDT] _/s///p' >> #{libname}.def" + + command = "gcc -nostdlib -o junk.o -Wl,--base-file,#{libname}.base,--dll " + + objs.join(" ") + " #{Init}.o " + command.concat(flags.join(" ") + + " -Wl,-e,_dll_entry@12 -lcygwin -lkernel32 #{CONFIG['srcdir']}/libruby.a") + xsystem command + + command = "dlltool --as=as --dllname #{libname}.dll --def #{libname}.def --base-file #{libname}.base --output-exp #{libname}.exp" + xsystem command + + command = "gcc -s -nostdlib -o #{libname}.dll -Wl,--dll #{libname}.exp " + + objs.join(" ") + " #{Init}.o " + command.concat(flags.join(" ") + + " -Wl,-e,_dll_entry@12 -lcygwin -lkernel32 #{CONFIG['srcdir']}/libruby.a") + xsystem command + File.unlink "junk.o" if FileTest.exist? "junk.o" + +else + # no special processing, just call ld + xsystem "ld #{args}" +end diff --git a/ext/dbm/dbm.c b/ext/dbm/dbm.c index b416802241..2764a325e1 100644 --- a/ext/dbm/dbm.c +++ b/ext/dbm/dbm.c @@ -6,7 +6,7 @@ $Date$ created at: Mon Jan 24 15:59:52 JST 1994 - Copyright (C) 1995 Yukihiro Matsumoto + Copyright (C) 1995-1998 Yukihiro Matsumoto ************************************************/ @@ -15,11 +15,12 @@ #include <ndbm.h> #include <fcntl.h> #include <errno.h> +#ifdef USE_CWGUSI +# include <sys/errno.h> +#endif VALUE cDBM; -extern VALUE mEnumerable; - struct dbmdata { int di_size; DBM *di_dbm; @@ -28,7 +29,7 @@ struct dbmdata { static void closed_dbm() { - Fail("closed DBM file"); + rb_raise(rb_eRuntimeError, "closed DBM file"); } #define GetDBM(obj, dbmp) {\ @@ -41,13 +42,14 @@ free_dbm(dbmp) struct dbmdata *dbmp; { if (dbmp->di_dbm) dbm_close(dbmp->di_dbm); + free(dbmp); } static VALUE -fdbm_s_open(argc, argv, class) +fdbm_s_open(argc, argv, klass) int argc; VALUE *argv; - VALUE class; + VALUE klass; { VALUE file, vmode; DBM *dbm; @@ -79,9 +81,10 @@ fdbm_s_open(argc, argv, class) rb_sys_fail(RSTRING(file)->ptr); } - obj = Data_Make_Struct(class,struct dbmdata,0,free_dbm,dbmp); + obj = Data_Make_Struct(klass,struct dbmdata,0,free_dbm,dbmp); dbmp->di_dbm = dbm; dbmp->di_size = -1; + rb_obj_call_init(obj); return obj; } @@ -118,23 +121,23 @@ fdbm_fetch(obj, keystr) if (value.dptr == 0) { return Qnil; } - return str_taint(str_new(value.dptr, value.dsize)); + return rb_tainted_str_new(value.dptr, value.dsize); } static VALUE -fdbm_indexes(obj, ag) - VALUE obj, ag; +fdbm_indexes(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; { - VALUE *p, *pend; VALUE new; - int i = 0; - struct RArray *args = RARRAY(rb_Array(ag)); + int i; - new = ary_new2(args->len); - p = args->ptr; pend = p + args->len; - while (p < pend) { - ary_push(new, fdbm_fetch(obj, *p++)); + new = rb_ary_new2(argc); + for (i=0; i<argc; i++) { + rb_ary_push(new, fdbm_fetch(obj, argv[i])); } + return new; } @@ -156,13 +159,13 @@ fdbm_delete(obj, keystr) value = dbm_fetch(dbm, key); if (value.dptr == 0) { - if (iterator_p()) rb_yield(keystr); + if (rb_iterator_p()) rb_yield(keystr); return Qnil; } if (dbm_delete(dbm, key)) { dbmp->di_size = -1; - Fail("dbm_delete failed"); + rb_raise(rb_eRuntimeError, "dbm_delete failed"); } else if (dbmp->di_size >= 0) { dbmp->di_size--; @@ -188,9 +191,9 @@ fdbm_shift(obj) val = dbm_fetch(dbm, key); dbm_delete(dbm, key); - keystr = str_taint(str_new(key.dptr, key.dsize)); - valstr = str_taint(str_new(val.dptr, val.dsize)); - return assoc_new(keystr, valstr); + keystr = rb_tainted_str_new(key.dptr, key.dsize); + valstr = rb_tainted_str_new(val.dptr, val.dsize); + return rb_assoc_new(keystr, valstr); } static VALUE @@ -207,11 +210,11 @@ fdbm_delete_if(obj) dbm = dbmp->di_dbm; for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); - keystr = str_taint(str_new(key.dptr, key.dsize)); - valstr = str_taint(str_new(val.dptr, val.dsize)); - if (RTEST(rb_yield(assoc_new(keystr, valstr)))) { + keystr = rb_tainted_str_new(key.dptr, key.dsize); + valstr = rb_tainted_str_new(val.dptr, val.dsize); + if (RTEST(rb_yield(rb_assoc_new(keystr, valstr)))) { if (dbm_delete(dbm, key)) { - Fail("dbm_delete failed"); + rb_raise(rb_eRuntimeError, "dbm_delete failed"); } } } @@ -232,13 +235,72 @@ fdbm_clear(obj) dbmp->di_size = -1; for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { if (dbm_delete(dbm, key)) { - Fail("dbm_delete failed"); + rb_raise(rb_eRuntimeError, "dbm_delete failed"); } } return obj; } static VALUE +fdbm_invert(obj) + VALUE obj; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + VALUE keystr, valstr; + VALUE hash = rb_hash_new(); + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + keystr = rb_tainted_str_new(key.dptr, key.dsize); + valstr = rb_tainted_str_new(val.dptr, val.dsize); + rb_hash_aset(hash, valstr, keystr); + } + return obj; +} + +static VALUE +each_pair(obj) + VALUE obj; +{ + return rb_funcall(obj, rb_intern("each_pair"), 0, 0); +} + +static VALUE fdbm_store _((VALUE,VALUE,VALUE)); + +static VALUE +update_i(pair, dbm) + VALUE pair, dbm; +{ + Check_Type(pair, T_ARRAY); + if (RARRAY(pair)->len < 2) { + rb_raise(rb_eArgError, "pair must be [key, value]"); + } + fdbm_store(dbm, RARRAY(pair)->ptr[0], RARRAY(pair)->ptr[1]); + return Qnil; +} + +static VALUE +fdbm_update(obj, other) + VALUE obj, other; +{ + rb_iterate(each_pair, other, update_i, obj); + return obj; +} + +static VALUE +fdbm_replace(obj, other) + VALUE obj, other; +{ + fdbm_clear(obj); + rb_iterate(each_pair, other, update_i, obj); + return obj; +} + +static VALUE fdbm_store(obj, keystr, valstr) VALUE obj, keystr, valstr; { @@ -252,14 +314,14 @@ fdbm_store(obj, keystr, valstr) } rb_secure(4); - keystr = obj_as_string(keystr); + keystr = rb_obj_as_string(keystr); key.dptr = RSTRING(keystr)->ptr; key.dsize = RSTRING(keystr)->len; if (NIL_P(valstr)) return fdbm_delete(obj, keystr); - valstr = obj_as_string(valstr); + valstr = rb_obj_as_string(valstr); val.dptr = RSTRING(valstr)->ptr; val.dsize = RSTRING(valstr)->len; @@ -267,9 +329,11 @@ fdbm_store(obj, keystr, valstr) dbmp->di_size = -1; dbm = dbmp->di_dbm; if (dbm_store(dbm, key, val, DBM_REPLACE)) { +#ifdef HAVE_DBM_CLAERERR dbm_clearerr(dbm); - if (errno == EPERM) rb_sys_fail(Qnil); - Fail("dbm_store failed"); +#endif + if (errno == EPERM) rb_sys_fail(0); + rb_raise(rb_eRuntimeError, "dbm_store failed"); } return valstr; @@ -316,8 +380,8 @@ fdbm_empty_p(obj) else { i = dbmp->di_size; } - if (i == 0) return TRUE; - return FALSE; + if (i == 0) return Qtrue; + return Qfalse; } static VALUE @@ -332,7 +396,7 @@ fdbm_each_value(obj) dbm = dbmp->di_dbm; for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); - rb_yield(str_taint(str_new(val.dptr, val.dsize))); + rb_yield(rb_tainted_str_new(val.dptr, val.dsize)); } return obj; } @@ -348,7 +412,7 @@ fdbm_each_key(obj) GetDBM(obj, dbmp); dbm = dbmp->di_dbm; for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { - rb_yield(str_taint(str_new(key.dptr, key.dsize))); + rb_yield(rb_tainted_str_new(key.dptr, key.dsize)); } return obj; } @@ -367,9 +431,9 @@ fdbm_each_pair(obj) for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); - keystr = str_taint(str_new(key.dptr, key.dsize)); - valstr = str_taint(str_new(val.dptr, val.dsize)); - rb_yield(assoc_new(keystr, valstr)); + keystr = rb_tainted_str_new(key.dptr, key.dsize); + valstr = rb_tainted_str_new(val.dptr, val.dsize); + rb_yield(rb_assoc_new(keystr, valstr)); } return obj; @@ -387,9 +451,9 @@ fdbm_keys(obj) GetDBM(obj, dbmp); dbm = dbmp->di_dbm; - ary = ary_new(); + ary = rb_ary_new(); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { - ary_push(ary, str_taint(str_new(key.dptr, key.dsize))); + rb_ary_push(ary, rb_tainted_str_new(key.dptr, key.dsize)); } return ary; @@ -407,10 +471,10 @@ fdbm_values(obj) GetDBM(obj, dbmp); dbm = dbmp->di_dbm; - ary = ary_new(); + ary = rb_ary_new(); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); - ary_push(ary, str_taint(str_new(val.dptr, val.dsize))); + rb_ary_push(ary, rb_tainted_str_new(val.dptr, val.dsize)); } return ary; @@ -431,8 +495,8 @@ fdbm_has_key(obj, keystr) GetDBM(obj, dbmp); dbm = dbmp->di_dbm; val = dbm_fetch(dbm, key); - if (val.dptr) return TRUE; - return FALSE; + if (val.dptr) return Qtrue; + return Qfalse; } static VALUE @@ -453,9 +517,9 @@ fdbm_has_value(obj, valstr) val = dbm_fetch(dbm, key); if (val.dsize == RSTRING(valstr)->len && memcmp(val.dptr, RSTRING(valstr)->ptr, val.dsize) == 0) - return TRUE; + return Qtrue; } - return FALSE; + return Qfalse; } static VALUE @@ -470,26 +534,29 @@ fdbm_to_a(obj) GetDBM(obj, dbmp); dbm = dbmp->di_dbm; - ary = ary_new(); + ary = rb_ary_new(); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); - ary_push(ary, assoc_new(str_taint(str_new(key.dptr, key.dsize)), - str_taint(str_new(val.dptr, val.dsize)))); + rb_ary_push(ary, rb_assoc_new(rb_tainted_str_new(key.dptr, key.dsize), + rb_tainted_str_new(val.dptr, val.dsize))); } return ary; } +void Init_dbm() { - cDBM = rb_define_class("DBM", cObject); - rb_include_module(cDBM, mEnumerable); + cDBM = rb_define_class("DBM", rb_cObject); + rb_include_module(cDBM, rb_mEnumerable); rb_define_singleton_method(cDBM, "open", fdbm_s_open, -1); + rb_define_singleton_method(cDBM, "new", fdbm_s_open, -1); rb_define_method(cDBM, "close", fdbm_close, 0); rb_define_method(cDBM, "[]", fdbm_fetch, 1); rb_define_method(cDBM, "[]=", fdbm_store, 2); - rb_define_method(cDBM, "indexes", fdbm_indexes, -2); + rb_define_method(cDBM, "indexes", fdbm_indexes, -1); + rb_define_method(cDBM, "indices", fdbm_indexes, -1); rb_define_method(cDBM, "length", fdbm_length, 0); rb_define_alias(cDBM, "size", "length"); rb_define_method(cDBM, "empty?", fdbm_empty_p, 0); @@ -503,6 +570,10 @@ Init_dbm() rb_define_method(cDBM, "delete", fdbm_delete, 1); rb_define_method(cDBM, "delete_if", fdbm_delete_if, 0); rb_define_method(cDBM, "clear", fdbm_clear, 0); + rb_define_method(cDBM,"invert", fdbm_invert, 0); + rb_define_method(cDBM,"update", fdbm_update, 1); + rb_define_method(cDBM,"replace", fdbm_replace, 1); + rb_define_method(cDBM, "include?", fdbm_has_key, 1); rb_define_method(cDBM, "has_key?", fdbm_has_key, 1); rb_define_method(cDBM, "has_value?", fdbm_has_value, 1); diff --git a/ext/dbm/extconf.rb b/ext/dbm/extconf.rb index 4a5d41f275..3a96cee12f 100644 --- a/ext/dbm/extconf.rb +++ b/ext/dbm/extconf.rb @@ -1,5 +1,9 @@ +require 'mkmf' $LDFLAGS = "-L/usr/local/lib" -have_library("gdbm", "dbm_open") or have_library("dbm", "dbm_open") +have_library("gdbm", "dbm_open") or + have_library("db", "dbm_open") or + have_library("dbm", "dbm_open") if have_func("dbm_open") + have_func("dbm_clearerr") create_makefile("dbm") end diff --git a/ext/etc/etc.c b/ext/etc/etc.c index 20cd9295ed..c10680c7d3 100644 --- a/ext/etc/etc.c +++ b/ext/etc/etc.c @@ -37,7 +37,7 @@ etc_getlogin(obj) #endif if (login) - return str_new2(login); + return rb_tainted_str_new2(login); return Qnil; } @@ -47,34 +47,36 @@ setup_passwd(pwd) struct passwd *pwd; { if (pwd == 0) rb_sys_fail("/etc/passwd"); - return struct_new(sPasswd, - str_new2(pwd->pw_name), - str_new2(pwd->pw_passwd), - INT2FIX(pwd->pw_uid), - INT2FIX(pwd->pw_gid), - str_new2(pwd->pw_gecos), - str_new2(pwd->pw_dir), - str_new2(pwd->pw_shell), + return rb_struct_new(sPasswd, + rb_tainted_str_new2(pwd->pw_name), + rb_tainted_str_new2(pwd->pw_passwd), + INT2FIX(pwd->pw_uid), + INT2FIX(pwd->pw_gid), +#ifdef PW_GECOS + rb_tainted_str_new2(pwd->pw_gecos), +#endif + rb_tainted_str_new2(pwd->pw_dir), + rb_tainted_str_new2(pwd->pw_shell), #ifdef PW_CHANGE - INT2FIX(pwd->pw_change), + INT2FIX(pwd->pw_change), #endif #ifdef PW_QUOTA - INT2FIX(pwd->pw_quota), + INT2FIX(pwd->pw_quota), #endif #ifdef PW_AGE - INT2FIX(pwd->pw_age), + INT2FIX(pwd->pw_age), #endif #ifdef PW_CLASS - str_new2(pwd->pw_class), + rb_tainted_str_new2(pwd->pw_class), #endif #ifdef PW_COMMENT - str_new2(pwd->pw_comment), + rb_tainted_str_new2(pwd->pw_comment), #endif #ifdef PW_EXPIRE - INT2FIX(pwd->pw_expire), + INT2FIX(pwd->pw_expire), #endif - 0 /*dummy*/ - ); + 0 /*dummy*/ + ); } #endif @@ -96,7 +98,7 @@ etc_getpwuid(argc, argv, obj) uid = getuid(); } pwd = getpwuid(uid); - if (pwd == 0) Fail("can't find user for %d", uid); + if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", uid); return setup_passwd(pwd); #else return Qnil; @@ -112,7 +114,7 @@ etc_getpwnam(obj, nam) Check_Type(nam, T_STRING); pwd = getpwnam(RSTRING(nam)->ptr); - if (pwd == 0) Fail("can't find user for %s", RSTRING(nam)->ptr); + if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %s", RSTRING(nam)->ptr); return setup_passwd(pwd); #else return Qnil; @@ -123,10 +125,10 @@ static VALUE etc_passwd(obj) VALUE obj; { -#if defined(HAVE_GETPWENT) && !defined(__CYGWIN32__) +#if defined(HAVE_GETPWENT) struct passwd *pw; - if (iterator_p()) { + if (rb_iterator_p()) { setpwent(); while (pw = getpwent()) { rb_yield(setup_passwd(pw)); @@ -135,7 +137,7 @@ etc_passwd(obj) return obj; } pw = getpwent(); - if (pw == 0) Fail("can't fetch next -- /etc/passwd"); + if (pw == 0) rb_raise(rb_eRuntimeError, "can't fetch next -- /etc/passwd"); return setup_passwd(pw); #else return Qnil; @@ -150,17 +152,17 @@ setup_group(grp) VALUE mem; char **tbl; - mem = ary_new(); + mem = rb_ary_new(); tbl = grp->gr_mem; while (*tbl) { - ary_push(mem, str_new2(*tbl)); + rb_ary_push(mem, rb_tainted_str_new2(*tbl)); tbl++; } - return struct_new(sGroup, - str_new2(grp->gr_name), - str_new2(grp->gr_passwd), - INT2FIX(grp->gr_gid), - mem); + return rb_struct_new(sGroup, + rb_tainted_str_new2(grp->gr_name), + rb_tainted_str_new2(grp->gr_passwd), + INT2FIX(grp->gr_gid), + mem); } #endif @@ -174,7 +176,7 @@ etc_getgrgid(obj, id) gid = NUM2INT(id); grp = getgrgid(gid); - if (grp == 0) Fail("can't find group for %d", gid); + if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", gid); return setup_group(grp); #else return Qnil; @@ -190,7 +192,7 @@ etc_getgrnam(obj, nam) Check_Type(nam, T_STRING); grp = getgrnam(RSTRING(nam)->ptr); - if (grp == 0) Fail("can't find group for %s", RSTRING(nam)->ptr); + if (grp == 0) rb_raise(rb_eArgError, "can't find group for %s", RSTRING(nam)->ptr); return setup_group(grp); #else return Qnil; @@ -204,7 +206,7 @@ etc_group(obj) #ifdef HAVE_GETGRENT struct group *grp; - if (iterator_p()) { + if (rb_iterator_p()) { setgrent(); while (grp = getgrent()) { rb_yield(setup_group(grp)); @@ -235,32 +237,35 @@ Init_etc() rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1); rb_define_module_function(mEtc, "group", etc_group, 0); - sPasswd = struct_define("Passwd", - "name", "passwd", "uid", "gid", - "gecos", "dir", "shell", + sPasswd = rb_struct_define("Passwd", + "name", "passwd", "uid", "gid", +#ifdef PW_GECOS + "gecos", +#endif + "dir", "shell", #ifdef PW_CHANGE - "change", + "change", #endif #ifdef PW_QUOTA - "quota", + "quota", #endif #ifdef PW_AGE - "age", + "age", #endif #ifdef PW_CLASS - "class", + "class", #endif #ifdef PW_COMMENT - "comment", + "comment", #endif #ifdef PW_EXPIRE - "expire", + "expire", #endif - 0); + 0); rb_global_variable(&sPasswd); #ifdef HAVE_GETGRENT - sGroup = struct_define("Group", "name", "passwd", "gid", "mem", 0); + sGroup = rb_struct_define("Group", "name", "passwd", "gid", "mem", 0); rb_global_variable(&sGroup); #endif } diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb index 884de93ec8..4cf04a3ec3 100644 --- a/ext/etc/extconf.rb +++ b/ext/etc/extconf.rb @@ -1,7 +1,31 @@ +require 'mkmf' + +def etc_grep_header(field) + f = open("conftest.c", "w") + f.print <<EOF +#include <pwd.h> +EOF + f.close + begin + if xsystem("#{CPP} | egrep #{field}") + $defs.push(format("-D%s", field.upcase)) + end + ensure + system "rm -f conftest.c" + end +end + have_library("sun", "getpwnam") # NIS (== YP) interface for IRIX 4 a = have_func("getlogin") b = have_func("getpwent") c = have_func("getgrent") if a or b or c + etc_grep_header("pw_gecos") + etc_grep_header("pw_change") + etc_grep_header("pw_quota") + etc_grep_header("pw_age") + etc_grep_header("pw_class") + etc_grep_header("pw_comment") + etc_grep_header("pw_expire") create_makefile("etc") end diff --git a/ext/extmk.rb.in b/ext/extmk.rb.in index e1d318d19c..058c144f94 100644 --- a/ext/extmk.rb.in +++ b/ext/extmk.rb.in @@ -1,12 +1,14 @@ #! /usr/local/bin/ruby -$".push 'mkmf.rb' +$".push 'mkmf.rb' #" +load '@top_srcdir@/lib/find.rb' if ARGV[0] == 'static' $force_static = TRUE ARGV.shift elsif ARGV[0] == 'install' $install = TRUE + $destdir = ARGV[1] || '' ARGV.shift elsif ARGV[0] == 'clean' $clean = TRUE @@ -19,8 +21,16 @@ $cache_mod = FALSE; $lib_cache = {} $func_cache = {} $hdr_cache = {} -$topdir = "@top_srcdir@" -if $topdir !~ "^/" +$top_srcdir = "@top_srcdir@" +if $top_srcdir !~ "^/" + # get absolute path + save = Dir.pwd + Dir.chdir $top_srcdir + $top_srcdir = Dir.pwd + Dir.chdir save +end +$topdir = ".." +if $topdir !~ "^/" # get absolute path save = Dir.pwd Dir.chdir $topdir @@ -59,23 +69,59 @@ end if PLATFORM == "m68k-human" CFLAGS = "@CFLAGS@".gsub(/-c..-stack=[0-9]+ */, '') -LINK = "@CC@ -o conftest -I#{$topdir} " + CFLAGS + " %s @LDFLAGS@ %s conftest.c @LIBS@ %s > nul 2>&1" -CPP = "@CPP@ @CPPFLAGS@ -I#{$topdir} " + CFLAGS + " %s conftest.c > nul 2>&1" else CFLAGS = "@CFLAGS@" -LINK = "@CC@ -o conftest -I#{$topdir} " + CFLAGS + " %s @LDFLAGS@ %s conftest.c %s > /dev/null 2>&1" -CPP = "@CPP@ @CPPFLAGS@ -I#{$topdir} " + CFLAGS + " %s conftest.c > /dev/null 2>&1" +end +LINK = "@CC@ -o conftest -I#$topdir -I#$top_srcdir -I@includedir@ #{CFLAGS} %s @LDFLAGS@ %s conftest.c @LIBS@ %s" +CPP = "@CPP@ @CPPFLAGS@ -I#$topdir -I#$top_srcdir -I@includedir@ #{CFLAGS} %s conftest.c" + +if /cygwin|mswin32|djgpp|mingw32|m68k-human/i =~ PLATFORM + $null = open("nul", "w") +else + $null = open("/dev/null", "w") +end + +$orgerr = $stderr.dup +$orgout = $stdout.dup +def xsystem command + if $DEBUG + return system(command) + end + $stderr.reopen($null) + $stdout.reopen($null) + r = system(command) + $stderr.reopen($orgerr) + $stdout.reopen($orgout) + return r end def try_link(libs) - system(format(LINK, $CFLAGS, $LDFLAGS, libs)) + xsystem(format(LINK, $CFLAGS, $LDFLAGS, libs)) end def try_cpp - system(format(CPP, $CFLAGS)) + xsystem(format(CPP, $CFLAGS)) +end + +def install_rb(mfile) + path = [] + dir = [] + Find.find("lib") do |f| + next unless /\.rb$/ =~ f + f = f[4..-1] + path.push f + dir |= File.dirname(f) + end + for f in dir + next if f == "." + mfile.printf "\t@test -d $(DESTDIR)$(pkglibdir)/%s || mkdir $(DESTDIR)$(pkglibdir)/%s\n", f, f + end + for f in path + mfile.printf "\t$(INSTALL_DATA) lib/%s $(DESTDIR)$(pkglibdir)/%s\n", f, f + end end -def have_library(lib, func) +def have_library(lib, func="main") if $lib_cache[lib] if $lib_cache[lib] == "yes" if $libs @@ -89,26 +135,34 @@ def have_library(lib, func) end end - cfile = open("conftest.c", "w") - cfile.printf "\ + if func && func != "" + cfile = open("conftest.c", "w") + cfile.printf "\ int main() { return 0; } int t() { %s(); return 0; } ", func - cfile.close + cfile.close - begin + begin + if $libs + libs = "-l" + lib + " " + $libs + else + libs = "-l" + lib + end + unless try_link(libs) + $lib_cache[lib] = 'no' + $cache_mod = TRUE + return FALSE + end + ensure + system "rm -f conftest*" + end + else if $libs libs = "-l" + lib + " " + $libs else libs = "-l" + lib end - unless try_link(libs) - $lib_cache[lib] = 'no' - $cache_mod = TRUE - return FALSE - end - ensure - system "rm -f conftest*" end $libs = libs @@ -206,9 +260,19 @@ def create_makefile(target) end $defs.push(format("-DEXTLIB='%s'", libs.join(","))) end - $libs = "" unless $libs - $srcdir = $topdir + "/ext/" + target + $DLDFLAGS = '@DLDFLAGS@' + + if PLATFORM =~ /beos/ + if $libs + $libs = $libs + " -lruby" + else + $libs = "-lruby" + end + $DLDFLAGS = $DLDFLAGS + " -L" + $topdir + end + + $srcdir = $top_srcdir + "/ext/" + target mfile = open("Makefile", "w") mfile.printf "\ SHELL = /bin/sh @@ -222,10 +286,11 @@ hdrdir = #{$topdir} CC = @CC@ -CFLAGS = %s -I#{$topdir} %s #$CFLAGS %s -DLDFLAGS = @DLDFLAGS@ #$LDFLAGS +prefix = @prefix@ +CFLAGS = %s -I#{$topdir} -I#{$top_srcdir} -I@includedir@ #{CFLAGS} #$CFLAGS %s +DLDFLAGS = #$DLDFLAGS @LDFLAGS@ #$LDFLAGS LDSHARED = @LDSHARED@ -", if $static then "" else "@CCDLFLAGS@" end, CFLAGS, $defs.join(" ") +", if $static then "" else "@CCDLFLAGS@" end, $defs.join(" ") mfile.printf "\ @@ -234,18 +299,20 @@ RUBY_INSTALL_NAME = `t='$(program_transform_name)'; echo ruby | sed $$t` prefix = @prefix@ exec_prefix = @exec_prefix@ -libdir = @libdir@/$(RUBY_INSTALL_NAME)/@arch@ +libdir = @libdir@ +pkglibdir = $(libdir)/$(RUBY_INSTALL_NAME) +archdir = $(pkglibdir)/@arch@ @SET_MAKE@ #### End of system configuration section. #### " - mfile.printf "LOCAL_LIBS = %s\n", $local_libs if $local_libs + mfile.printf "LOCAL_LIBS = %s\n", $local_libs unless $local_libs == "" mfile.printf "LIBS = %s\n", $libs mfile.printf "OBJS = " if !$objs then $objs = [] - for f in Dir["#{$topdir}/ext/#{target}/*.{c,cc}"] + for f in Dir["#{$top_srcdir}/ext/#{$mdir}/*.{c,cc}"] f = File.basename(f) f.sub!(/\.(c|cc)$/, ".o") $objs.push f @@ -254,42 +321,48 @@ libdir = @libdir@/$(RUBY_INSTALL_NAME)/@arch@ mfile.printf $objs.join(" ") mfile.printf "\n" - mfile.printf "\ -TARGET = %s.%s + mfile.printf <<EOS +TARGET = #{target}.#{$static ? "a" : "@DLEXT@"} -INSTALL = %s@INSTALL@ +INSTALL = #{$dots}@INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ binsuffix = @binsuffix@ all: $(TARGET) -clean:; @rm -f *.o *.so *.sl +clean:; @rm -f *.o *.a *.so *.sl @rm -f Makefile extconf.h conftest.* @rm -f core ruby$(binsuffix) *~ realclean: clean -", target, - if $static then "o" else "@DLEXT@" end, $dots +EOS - mfile.printf "\ + mfile.printf <<EOS install: -" + @test -d $(DESTDIR)$(libdir) || mkdir $(DESTDIR)$(libdir) + @test -d $(DESTDIR)$(pkglibdir) || mkdir $(DESTDIR)$(pkglibdir) + @test -d $(DESTDIR)$(archdir) || mkdir $(DESTDIR)$(archdir) +EOS if !$static - mfile.printf " - @test -d $(libdir) || mkdir $(libdir) - $(INSTALL) $(TARGET) $(libdir)/$(TARGET) + mfile.printf "\ + $(INSTALL) $(TARGET) $(DESTDIR)$(archdir)/$(TARGET) " end - for rb in Dir["lib/*.rb"] - mfile.printf "\t$(INSTALL) %s @libdir@/$(RUBY_INSTALL_NAME)\n", rb - end + install_rb(mfile) mfile.printf "\n" - if !$static && "@DLEXT@" != "o" + if $static + mfile.printf "\ +$(TARGET): $(OBJS) + @AR@ cru $(TARGET) $(OBJS) + @-@RANLIB@ $(TARGET) 2> /dev/null || true +" + elsif "@DLEXT@" != "o" mfile.printf "\ $(TARGET): $(OBJS) - $(LDSHARED) $(DLDFLAGS) -o $(TARGET) $(OBJS) $(LOCAL_LIBS) $(LIBS) + $(LDSHARED) $(DLDFLAGS) -o $(TARGET) $(OBJS) $(LIBS) $(LOCAL_LIBS) " elsif not File.exist?(target + ".c") and not File.exist?(target + ".cc") if PLATFORM == "m68k-human" @@ -297,16 +370,11 @@ $(TARGET): $(OBJS) $(TARGET): $(OBJS) ar cru $(TARGET) $(OBJS) " - elsif PLATFORM =~ "-nextstep" + elsif PLATFORM =~ "-nextstep" || PLATFORM =~ "-openstep" || PLATFORM =~ "-rhapsody" mfile.printf "\ $(TARGET): $(OBJS) cc -r $(CFLAGS) -o $(TARGET) $(OBJS) " - elsif $static - mfile.printf "\ -$(TARGET): $(OBJS) - ld -r -o $(TARGET) $(OBJS) -" else mfile.printf "\ $(TARGET): $(OBJS) @@ -324,6 +392,19 @@ $(TARGET): $(OBJS) dfile.close end mfile.close + + if PLATFORM =~ /beos/ + if PLATFORM =~ /^powerpc/ then + deffilename = "ruby.exp" + else + deffilename = "ruby.def" + end + print "creating ruby.def\n" + open(deffilename, "w") do |file| + file.print("EXPORTS\n") if PLATFORM =~ /^i/ + file.print("Init_#{target}\n") + end + end end def extmake(target) @@ -335,24 +416,25 @@ def extmake(target) return if $nodynamic and not $static - $local_libs = nil - $libs = nil $objs = nil - $CFLAGS = nil - $LDFLAGS = nil + $libs = PLATFORM =~ /cygwin|beos|openstep|nextstep|rhapsody/ ? nil : "-lc" + $local_libs = "" # to be assigned in extconf.rb + $CFLAGS = "" + $LDFLAGS = "" begin - system "mkdir " + target unless File.directory?(target) + system "mkdir", target unless File.directory?(target) Dir.chdir target + $mdir = target if $static_ext.size > 0 || !File.exist?("./Makefile") || - older("./Makefile", "#{$topdir}/ext/@setup@") || + older("./Makefile", "#{$top_srcdir}/ext/@setup@") || older("./Makefile", "../extmk.rb") || - older("./Makefile", "#{$topdir}/ext/#{target}/extconf.rb") + older("./Makefile", "#{$top_srcdir}/ext/#{target}/extconf.rb") then $defs = [] - if File.exist?("#{$topdir}/ext/#{target}/extconf.rb") - load "#{$topdir}/ext/#{target}/extconf.rb" + if File.exist?("#{$top_srcdir}/ext/#{target}/extconf.rb") + load "#{$top_srcdir}/ext/#{target}/extconf.rb" else create_makefile(target); end @@ -362,7 +444,7 @@ def extmake(target) $extlist.push [$static,target] end if $install - system "make install" + system "make install DESTDIR=#{$destdir}" elsif $clean system "make clean" else @@ -370,9 +452,10 @@ def extmake(target) end end if $static - $extlibs += " " + $LDFLAGS if $LDFLAGS - $extlibs += " " + $local_libs if $local_libs + $extlibs ||= "" + $extlibs += " " + $LDFLAGS unless $LDFLAGS == "" $extlibs += " " + $libs if $libs + $extlibs += " " + $local_libs unless $local_libs == "" end ensure Dir.chdir ".." @@ -381,11 +464,11 @@ end # get static-link modules $static_ext = {} -for setup in ["@setup@", "#{$topdir}/ext/@setup@"] +for setup in ["@setup@", "#{$top_srcdir}/ext/@setup@"] if File.file? setup f = open(setup) while f.gets() - $_.chop! + $_.chomp! sub!(/#.*$/, '') next if /^\s*$/ if /^option +nodynamic/ @@ -399,7 +482,7 @@ for setup in ["@setup@", "#{$topdir}/ext/@setup@"] end end -for d in Dir["#{$topdir}/ext/*"] +for d in Dir["#{$top_srcdir}/ext/*"] File.directory?(d) || next File.file?(d + "/MANIFEST") || next @@ -429,15 +512,16 @@ if $cache_mod end exit if $install or $clean -$extinit += "" +$extinit = "" unless $extinit if $extlist.size > 0 for s,t in $extlist - f = format("%s/%s.o", s, t) + f = format("%s/%s.a", s, t) if File.exist?(f) $extinit += format("\ \tInit_%s();\n\ \trb_provide(\"%s.o\");\n\ ", t, t) + $extobjs = "" unless $extobjs $extobjs += "ext/" $extobjs += f $extobjs += " " @@ -446,7 +530,7 @@ if $extlist.size > 0 end end - if older("extinit.c", "#{$topdir}/ext/@setup@") + if older("extinit.c", "#{$top_srcdir}/ext/@setup@") f = open("extinit.c", "w") f.printf "void Init_ext() {\n" f.printf $extinit @@ -461,17 +545,24 @@ if $extlist.size > 0 Dir.chdir ".." - if older("ruby@binsuffix@", "#{$topdir}/ext/@setup@") or older("ruby@binsuffix@", "miniruby@binsuffix@") + if older("ruby@binsuffix@", "#{$top_srcdir}/ext/@setup@") or older("ruby@binsuffix@", "miniruby@binsuffix@") `rm -f ruby@binsuffix@` end - $extobjs = "ext/extinit.o " + $extobjs + if $extobjs + $extobjs = "ext/extinit.o " + $extobjs + else + $extobjs = "ext/extinit.o " + end + if PLATFORM =~ /m68k-human|beos/ + $extlibs.gsub!("-L/usr/local/lib", "") if $extlibs + end system format('make ruby@binsuffix@ EXTOBJS="%s" EXTLIBS="%s"', $extobjs, $extlibs) else Dir.chdir ".." if older("ruby@binsuffix@", "miniruby@binsuffix@") `rm -f ruby@binsuffix@` - `cp miniruby@binsuffix@ ruby@binsuffix@` + system("make ruby@binsuffix@") end end diff --git a/ext/extmk.rb.nt b/ext/extmk.rb.nt index 6792f2717b..5836e5c681 100644 --- a/ext/extmk.rb.nt +++ b/ext/extmk.rb.nt @@ -1,5 +1,7 @@ #! /usr/local/bin/ruby +$".push 'mkmf.rb' #" + if ARGV[0] == 'static' $force_static = TRUE ARGV.shift @@ -379,7 +381,7 @@ def extmake(target) end end if $static - #$extlibs = " " + $extlibs = " " $extlibs += " " + $LDFLAGS if $LDFLAGS $extlibs += " " + $local_libs if $local_libs $extlibs += " " + $libs if $libs @@ -438,6 +440,8 @@ if $cache_mod end exit if $install or $clean +$extinit = " " unless $extinit +$extobjs = "" if $extlist.size > 0 for s,t in $extlist #for s,t in $static_ext diff --git a/ext/fcntl/fcntl.c b/ext/fcntl/fcntl.c index 17aacb13c3..186f9ac893 100644 --- a/ext/fcntl/fcntl.c +++ b/ext/fcntl/fcntl.c @@ -5,7 +5,7 @@ $Author$ created at: Mon Apr 7 18:53:05 JST 1997 - Copyright (C) 1997 Yukihiro Matsumoto + Copyright (C) 1997-1998 Yukihiro Matsumoto ************************************************/ diff --git a/ext/kconv/kconv.c b/ext/kconv/kconv.c index 6778afcfe6..a3349826f1 100644 --- a/ext/kconv/kconv.c +++ b/ext/kconv/kconv.c @@ -1780,12 +1780,30 @@ kconv_kconv(argc, argv) VALUE src, dst; VALUE in, out; int in_code, out_code; + char *codename = 0; rb_scan_args(argc, argv, "12", &src, &out, &in); Check_Type(src, T_STRING); if (NIL_P(out)) { - out_code = _JIS; + codename = rb_get_kcode(); + goto codeselect; + } + else if (TYPE(out) == T_STRING) { + codename = RSTRING(out)->ptr; + codeselect: + switch (codename[0]) { + case 'E': case 'e': + out_code = _EUC; + break; + case 'S': case 's': + out_code = _SJIS; + break; + case 'J': case 'j': + default: + out_code = _JIS; + break; + } } else { out_code = NUM2INT(out); @@ -1794,12 +1812,28 @@ kconv_kconv(argc, argv) if (NIL_P(in)) { in_code = _AUTO; } + else if (TYPE(in) == T_STRING) { + switch (RSTRING(in)->ptr[0]) { + case 'E': case 'e': + in_code = _EUC; + break; + case 'S': case 's': + in_code = _SJIS; + break; + case 'J': case 'j': + in_code = _JIS; + break; + default: + in_code = _AUTO; + break; + } + } else { in_code = NUM2INT(in); if (in_code == _NOCONV) return (VALUE)src; } - dst = str_new(0, RSTRING(src)->len*3+10); /* large enough? */ + dst = rb_str_new(0, RSTRING(src)->len*3+10); /* large enough? */ RSTRING(dst)->len = do_kconv(RSTRING(src)->ptr, RSTRING(dst)->ptr, RSTRING(dst)->len, out_code, in_code); return dst; @@ -1813,7 +1847,7 @@ kconv_tojis(obj, src) Check_Type(src, T_STRING); - dst = str_new(0, RSTRING(src)->len*3+10); /* large enough? */ + dst = rb_str_new(0, RSTRING(src)->len*3+10); /* large enough? */ RSTRING(dst)->len = do_kconv(RSTRING(src)->ptr, RSTRING(dst)->ptr, RSTRING(dst)->len, _JIS, _AUTO); return dst; @@ -1827,7 +1861,7 @@ kconv_toeuc(obj, src) Check_Type(src, T_STRING); - dst = str_new(0, RSTRING(src)->len*3+10); /* large enough? */ + dst = rb_str_new(0, RSTRING(src)->len*3+10); /* large enough? */ RSTRING(dst)->len = do_kconv(RSTRING(src)->ptr, RSTRING(dst)->ptr, RSTRING(dst)->len, _EUC, _AUTO); return (VALUE)dst; @@ -1841,7 +1875,7 @@ kconv_tosjis(obj, src) Check_Type(src, T_STRING); - dst = str_new(0, RSTRING(src)->len*3+10); /* large enough? */ + dst = rb_str_new(0, RSTRING(src)->len*3+10); /* large enough? */ RSTRING(dst)->len = do_kconv(RSTRING(src)->ptr, RSTRING(dst)->ptr, RSTRING(dst)->len, _SJIS, _AUTO); return dst; @@ -1857,10 +1891,29 @@ static VALUE kconv_guess(obj, src) VALUE obj, src; { - unsigned char *p = RSTRING(src)->ptr; - unsigned char *pend = p + RSTRING(src)->len; + unsigned char *p; + unsigned char *pend; + int sequence_counter = 0; + + Check_Type(src, T_STRING); + + p = RSTRING(src)->ptr; + pend = p + RSTRING(src)->len; + +#define INCR do {\ + p++;\ + if (p==pend) return INT2FIX(_UNKNOWN);\ + sequence_counter++;\ + if (sequence_counter % 2 == 1 && *p != 0xa4)\ + sequence_counter = 0;\ + if (6 <= sequence_counter) {\ + sequence_counter = 0;\ + return INT2FIX(_EUC);\ + }\ +} while (0) -#define INCR {p++;if (p==pend) return INT2FIX(_UNKNOWN);} + if (*p == 0xa4) + sequence_counter = 1; while (p<pend) { if (*p == '\033') { @@ -1874,37 +1927,41 @@ kconv_guess(obj, src) if (0x81 <= *p && *p <= 0x8d) { return INT2FIX(_SJIS); } - if (*p == 0x8e) { + if (0x8f <= *p && *p <= 0x9f) { + return INT2FIX(_SJIS); + } + if (*p == 0x8e) { /* SS2 */ INCR; if ((0x40 <= *p && *p <= 0x7e) || (0x80 <= *p && *p <= 0xa0) || (0xe0 <= *p && *p <= 0xfc)) return INT2FIX(_SJIS); } - if (0xa1 <= *p && *p <= 0xdf) { + else if (0xa1 <= *p && *p <= 0xdf) { INCR; if (0xf0 <= *p && *p <= 0xfe) return INT2FIX(_EUC); if (0xe0 <= *p && *p <= 0xef) { - while (*p >= 0x40) { + while (p < pend && *p >= 0x40) { if (*p >= 0x81) { - if (0x8d <= *p || (0x8f <= *p && *p <= 0x9f)) { + if (*p <= 0x8d || (0x8f <= *p && *p <= 0x9f)) { return INT2FIX(_SJIS); } else if (0xfd <= *p && *p <= 0xfe) { return INT2FIX(_EUC); } } + INCR; } } - if (*p <= 0x9f) { + else if (*p <= 0x9f) { return INT2FIX(_SJIS); } } - if (0xf0 <= *p && *p <= 0xfe) { + else if (0xf0 <= *p && *p <= 0xfe) { return INT2FIX(_EUC); } - if (0xe0 <= *p && *p <= 0xef) { + else if (0xe0 <= *p && *p <= 0xef) { INCR; if ((0x40 <= *p && *p <= 0x7e) || (0x80 <= *p && *p <= 0xa0)) { @@ -1914,7 +1971,7 @@ kconv_guess(obj, src) return INT2FIX(_EUC); } } - p++; + INCR; } return INT2FIX(_UNKNOWN); } diff --git a/ext/md5/md5init.c b/ext/md5/md5init.c index 47f913792f..a825f96d47 100644 --- a/ext/md5/md5init.c +++ b/ext/md5/md5init.c @@ -5,7 +5,7 @@ $Author$ created at: Fri Aug 2 09:24:12 JST 1996 - Copyright (C) 1995 Yukihiro Matsumoto + Copyright (C) 1995-1998 Yukihiro Matsumoto ************************************************/ /* This module provides an interface to the RSA Data Security, @@ -42,7 +42,7 @@ md5_digest(obj) ctx = *md5; MD5Final(digest, &ctx); - return str_new(digest, 16); + return rb_str_new(digest, 16); } static VALUE @@ -53,7 +53,7 @@ md5_clone(obj) MD5_CTX *md5, *md5_new; Data_Get_Struct(obj, MD5_CTX, md5); - obj = Data_Make_Struct(CLASS_OF(obj), MD5_CTX, 0, 0, md5_new); + obj = Data_Make_Struct(CLASS_OF(obj), MD5_CTX, 0, free, md5_new); *md5_new = *md5; return obj; @@ -61,6 +61,9 @@ md5_clone(obj) static VALUE md5_new(argc, argv, class) + int argc; + VALUE* argv; + VALUE class; { int i; VALUE arg, obj; @@ -69,18 +72,19 @@ md5_new(argc, argv, class) rb_scan_args(argc, argv, "01", &arg); if (!NIL_P(arg)) Check_Type(arg, T_STRING); - obj = Data_Make_Struct(class, MD5_CTX, 0, 0, md5); + obj = Data_Make_Struct(class, MD5_CTX, 0, free, md5); MD5Init(md5); if (!NIL_P(arg)) { md5_update(obj, arg); } + rb_obj_call_init(obj); return obj; } Init_md5() { - cMD5 = rb_define_class("MD5", cObject); + cMD5 = rb_define_class("MD5", rb_cObject); rb_define_singleton_method(cMD5, "new", md5_new, -1); diff --git a/ext/socket/depend b/ext/socket/depend index 3d54fa073c..6e8c3b7d97 100644 --- a/ext/socket/depend +++ b/ext/socket/depend @@ -1 +1 @@ -socket.o : socket.c $(hdrdir)/ruby.h $(hdrdir)/config.h $(hdrdir)/defines.h $(hdrdir)/io.h $(hdrdir)/sig.h +socket.o : socket.c $(hdrdir)/ruby.h $(hdrdir)/config.h $(hdrdir)/defines.h $(hdrdir)/rubyio.h $(hdrdir)/rubysig.h diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb index f719723864..449d5a2785 100644 --- a/ext/socket/extconf.rb +++ b/ext/socket/extconf.rb @@ -1,18 +1,22 @@ -$LDFLAGS = "-L/usr/local/lib" +require 'mkmf' +$LDFLAGS = "-L/usr/local/lib" if File.directory?("/usr/local/lib") case PLATFORM when /mswin32/ test_func = "WSACleanup" have_library("wsock32", "WSACleanup") when /cygwin32/ - test_func = "cygwin32_socket" + test_func = "socket" +when /beos/ + test_func = "socket" + have_library("net", "socket") else test_func = "socket" + have_library("nsl", "t_open") have_library("socket", "socket") - have_library("inet", "gethostbyname") - have_library("nsl", "gethostbyname") end have_header("sys/un.h") if have_func(test_func) + have_func("inet_aton") have_func("hsterror") unless have_func("gethostname") have_func("uname") diff --git a/ext/socket/socket.c b/ext/socket/socket.c index f5d191b056..e9bdbc9e8c 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -9,7 +9,8 @@ ************************************************/ #include "ruby.h" -#include "io.h" +#include "rubyio.h" +#include "rubysig.h" #include <stdio.h> #include <sys/types.h> #ifndef NT @@ -22,7 +23,14 @@ #include <sys/un.h> #endif -#if defined(THREAD) && defined(HAVE_FCNTL) +#ifdef USE_CWGUSI +extern int fileno(FILE *stream); /* <unix.mac.h> */ +extern int rb_thread_select(int, fd_set*, fd_set*, fd_set*, struct timeval*); /* thread.c */ +# include <sys/errno.h> +# include <GUSI.h> +#endif + +#if defined(USE_THREAD) && defined(HAVE_FCNTL) #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif @@ -34,30 +42,25 @@ #define EWOULDBLOCK EAGAIN #endif -extern VALUE cIO; -extern VALUE cInteger; - -VALUE cBasicSocket; -VALUE cIPsocket; -VALUE cTCPsocket; -VALUE cTCPserver; -VALUE cUDPsocket; +VALUE rb_cBasicSocket; +VALUE rb_cIPsocket; +VALUE rb_cTCPsocket; +VALUE rb_cTCPserver; +VALUE rb_cUDPsocket; #ifdef AF_UNIX -VALUE cUNIXsocket; -VALUE cUNIXserver; +VALUE rb_cUNIXsocket; +VALUE rb_cUNIXserver; #endif -VALUE cSocket; +VALUE rb_cSocket; -extern VALUE eException; -static VALUE eSocket; +static VALUE rb_eSocket; #ifdef SOCKS -VALUE cSOCKSsocket; +VALUE rb_cSOCKSsocket; void SOCKSinit(); int Rconnect(); #endif -FILE *rb_fdopen(); char *strdup(); #define INET_CLIENT 0 @@ -69,9 +72,12 @@ static void sock_finalize(fptr) OpenFile *fptr; { - SOCKET s = fileno(fptr->f); + SOCKET s; + + if (!fptr->f) return; + s = fileno(fptr->f); free(fptr->f); - free(fptr->f2); + if (fptr->f2) free(fptr->f2); closesocket(s); } #endif @@ -85,6 +91,7 @@ sock_new(class, fd) NEWOBJ(sock, struct RFile); OBJSETUP(sock, class, T_FILE); + rb_secure(4); MakeOpenFile(sock, fp); fp->f = rb_fdopen(fd, "r"); #ifdef NT @@ -92,7 +99,8 @@ sock_new(class, fd) #endif fp->f2 = rb_fdopen(fd, "w"); fp->mode = FMODE_READWRITE; - io_unbuffered(fp); + rb_io_unbuffered(fp); + rb_obj_call_init((VALUE)sock); return (VALUE)sock; } @@ -107,12 +115,15 @@ bsock_shutdown(argc, argv, sock) int how; OpenFile *fptr; + rb_secure(4); rb_scan_args(argc, argv, "01", &howto); if (howto == Qnil) how = 2; else { how = NUM2INT(howto); - if (how < 0 && how > 2) how = 2; + if (how < 0 || 2 < how) { + rb_raise(rb_eArgError, "`how' should be either 0, 1, 2"); + } } GetOpenFile(sock, fptr); if (shutdown(fileno(fptr->f), how) == -1) @@ -122,9 +133,57 @@ bsock_shutdown(argc, argv, sock) } static VALUE +bsock_close_read(sock) + VALUE sock; +{ + OpenFile *fptr; + + rb_secure(4); + GetOpenFile(sock, fptr); + if (fptr->f2 == 0) { + return rb_io_close(sock); + } + if (shutdown(fileno(fptr->f), 0) == -1) + rb_sys_fail(0); + fptr->mode &= ~FMODE_READABLE; +#ifdef NT + free(fptr->f); +#else + fclose(fptr->f); +#endif + fptr->f = fptr->f2; + fptr->f2 = 0; + + return Qnil; +} + +static VALUE +bsock_close_write(sock) + VALUE sock; +{ + OpenFile *fptr; + + rb_secure(4); + GetOpenFile(sock, fptr); + if (fptr->f2 == 0) { + return rb_io_close(sock); + } + if (shutdown(fileno(fptr->f), 1) == -1) + rb_sys_fail(0); + fptr->mode &= ~FMODE_WRITABLE; +#ifdef NT + free(fptr->f2); +#else + fclose(fptr->f2); +#endif + fptr->f2 = 0; + + return Qnil; +} + +static VALUE bsock_setsockopt(sock, lev, optname, val) - VALUE sock, lev, optname; - struct RString *val; + VALUE sock, lev, optname, val; { int level, option; OpenFile *fptr; @@ -148,8 +207,7 @@ bsock_setsockopt(sock, lev, optname, val) v = (char*)&i; vlen = sizeof(i); break; default: - Check_Type(val, T_STRING); - v = val->ptr; vlen = val->len; + v = rb_str2cstr(val, &vlen); } GetOpenFile(sock, fptr); @@ -163,6 +221,7 @@ static VALUE bsock_getsockopt(sock, lev, optname) VALUE sock, lev, optname; { +#if !defined(__BEOS__) int level, option, len; char *buf; OpenFile *fptr; @@ -176,7 +235,10 @@ bsock_getsockopt(sock, lev, optname) if (getsockopt(fileno(fptr->f), level, option, buf, &len) < 0) rb_sys_fail(fptr->path); - return str_new(buf, len); + return rb_str_new(buf, len); +#else + rb_notimplement(); +#endif } static VALUE @@ -190,7 +252,7 @@ bsock_getsockname(sock) GetOpenFile(sock, fptr); if (getsockname(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0) rb_sys_fail("getsockname(2)"); - return str_new(buf, len); + return rb_str_new(buf, len); } static VALUE @@ -204,7 +266,7 @@ bsock_getpeername(sock) GetOpenFile(sock, fptr); if (getpeername(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0) rb_sys_fail("getpeername(2)"); - return str_new(buf, len); + return rb_str_new(buf, len); } static VALUE @@ -213,31 +275,32 @@ bsock_send(argc, argv, sock) VALUE *argv; VALUE sock; { - struct RString *msg, *to; + VALUE msg, to; VALUE flags; OpenFile *fptr; FILE *f; int fd, n; + char *m, *t; + int mlen, tlen; rb_secure(4); rb_scan_args(argc, argv, "21", &msg, &flags, &to); - Check_Type(msg, T_STRING); - GetOpenFile(sock, fptr); - f = fptr->f2?fptr->f2:fptr->f; + f = GetWriteFile(fptr); fd = fileno(f); retry: -#ifdef THREAD - thread_fd_writable(fd); +#ifdef USE_THREAD + rb_thread_fd_writable(fd); #endif + m = rb_str2cstr(msg, &mlen); if (RTEST(to)) { - Check_Type(to, T_STRING); - n = sendto(fd, msg->ptr, msg->len, NUM2INT(flags), - (struct sockaddr*)to->ptr, to->len); + t = rb_str2cstr(to, &tlen); + n = sendto(fd, m, mlen, NUM2INT(flags), + (struct sockaddr*)t, tlen); } else { - n = send(fd, msg->ptr, msg->len, NUM2INT(flags)); + n = send(fd, m, mlen, NUM2INT(flags)); } if (n < 0) { switch (errno) { @@ -246,8 +309,8 @@ bsock_send(argc, argv, sock) #if EAGAIN != EWOULDBLOCK case EAGAIN: #endif -#ifdef THREAD - thread_schedule(); +#ifdef USE_THREAD + rb_thread_schedule(); #endif goto retry; } @@ -277,7 +340,6 @@ s_recv(sock, argc, argv, from) enum sock_recv_type from; { OpenFile *fptr; - FILE f; VALUE str; char buf[1024]; int fd, alen = sizeof buf; @@ -289,12 +351,12 @@ s_recv(sock, argc, argv, from) if (flg == Qnil) flags = 0; else flags = NUM2INT(flg); - str = str_new(0, NUM2INT(len)); + str = rb_str_new(0, NUM2INT(len)); GetOpenFile(sock, fptr); fd = fileno(fptr->f); -#ifdef THREAD - thread_wait_fd(fd); +#ifdef USE_THREAD + rb_thread_wait_fd(fd); #endif TRAP_BEG; retry: @@ -309,38 +371,35 @@ s_recv(sock, argc, argv, from) #if EAGAIN != EWOULDBLOCK case EAGAIN: #endif -#ifdef THREAD - thread_schedule(); +#ifdef USE_THREAD + rb_thread_schedule(); #endif goto retry; } rb_sys_fail("recvfrom(2)"); } - str_taint(str); + rb_obj_taint(str); switch (from) { case RECV_RECV: return (VALUE)str; case RECV_TCP: if (alen != sizeof(struct sockaddr_in)) { - TypeError("sockaddr size differs - should not happen"); + rb_raise(rb_eTypeError, "sockaddr size differs - should not happen"); } - return assoc_new(str, ipaddr((struct sockaddr_in *)buf)); + return rb_assoc_new(str, ipaddr((struct sockaddr_in *)buf)); case RECV_UDP: { VALUE addr = ipaddr((struct sockaddr_in *)buf); - return assoc_new(str, assoc_new(RARRAY(addr)->ptr[2], - RARRAY(addr)->ptr[1])); + return rb_assoc_new(str, rb_assoc_new(RARRAY(addr)->ptr[2], + RARRAY(addr)->ptr[1])); } #ifdef HAVE_SYS_UN_H case RECV_UNIX: - if (alen != sizeof(struct sockaddr_un)) { - TypeError("sockaddr size differs - should not happen"); - } - return assoc_new(str, unixaddr((struct sockaddr_un *)buf)); + return rb_assoc_new(str, unixaddr((struct sockaddr_un *)buf)); #endif case RECV_SOCKET: - return assoc_new(str, str_new(buf, alen)); + return rb_assoc_new(str, rb_str_new(buf, alen)); } } @@ -353,7 +412,96 @@ bsock_recv(argc, argv, sock) return s_recv(sock, argc, argv, RECV_RECV); } -#if defined(THREAD) && defined(HAVE_FCNTL) +static VALUE +mkipaddr(x) + unsigned long x; +{ + char buf[16]; + + x = ntohl(x); + sprintf(buf, "%d.%d.%d.%d", + (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, + (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); + return rb_str_new2(buf); +} + +static VALUE +ipaddr(sockaddr) + struct sockaddr_in *sockaddr; +{ + VALUE family, port, addr1, addr2; + VALUE ary; + struct hostent *hostent; + + family = rb_str_new2("AF_INET"); + hostent = gethostbyaddr((char*)&sockaddr->sin_addr.s_addr, + sizeof(sockaddr->sin_addr), + AF_INET); + addr1 = 0; + if (hostent) { + addr1 = rb_str_new2(hostent->h_name); + } + addr2 = mkipaddr(sockaddr->sin_addr.s_addr); + if (!addr1) addr1 = addr2; + + port = INT2FIX(ntohs(sockaddr->sin_port)); + ary = rb_ary_new3(4, family, port, addr1, addr2); + + return ary; +} + +#ifndef HAVE_INET_ATON +static unsigned long +inet_aton(host, inp) + char *host; + struct in_addr *inp; +{ + int d1, d2, d3, d4; + char ch; + + if (sscanf(host, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 && + 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 && + 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) { + inp->s_addr = htonl( + ((long) d1 << 24) | ((long) d2 << 16) | + ((long) d3 << 8) | ((long) d4 << 0)); + return 1; + } + return 0; +} +#endif + +static void +setipaddr(name, addr) + char *name; + struct sockaddr_in *addr; +{ + struct hostent *hp; + + if (name[0] == 0) { + addr->sin_addr.s_addr = INADDR_ANY; + } + else if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) { + addr->sin_addr.s_addr = INADDR_BROADCAST; + } + else if (inet_aton(name, &addr->sin_addr) != 0) { + /* ok to set addr->sin_addr */ + } + else { + hp = gethostbyname(name); + if (!hp) { +#ifdef HAVE_HSTRERROR + extern int h_errno; + rb_raise(rb_eSocket, (char *)hstrerror(h_errno)); +#else + rb_raise(rb_eSocket, "host not found"); +#endif + } + memcpy((char *) &addr->sin_addr, hp->h_addr, hp->h_length); + } +} + +#if defined(USE_THREAD) && defined(HAVE_FCNTL) static int thread_connect(fd, sockaddr, len, type) int fd; @@ -396,7 +544,7 @@ thread_connect(fd, sockaddr, len, type) #endif FD_ZERO(&fds); FD_SET(fd, &fds); - thread_select(fd+1, 0, &fds, 0, 0, 0); + rb_thread_select(fd+1, 0, &fds, 0, 0); continue; #endif @@ -434,26 +582,25 @@ open_inet(class, h, serv, type) int hostaddr, hostaddrPtr[2]; int servport; char *syscall; - VALUE sock; if (h) { Check_SafeStr(h); host = RSTRING(h)->ptr; hostent = gethostbyname(host); if (hostent == NULL) { - hostaddr = inet_addr(host); - if (hostaddr == -1) { + if (!inet_aton(host, &sockaddr.sin_addr)) { if (type == INET_SERVER && !strlen(host)) hostaddr = INADDR_ANY; else { #ifdef HAVE_HSTRERROR extern int h_errno; - Raise(eSocket, (char *)hstrerror(h_errno)); + rb_raise(rb_eSocket, (char *)hstrerror(h_errno)); #else - Raise(eSocket, "host not found"); + rb_raise(rb_eSocket, "host not found"); #endif } } + hostaddr = sockaddr.sin_addr.s_addr; _hostent.h_addr_list = (char **)hostaddrPtr; _hostent.h_addr_list[0] = (char *)&hostaddr; _hostent.h_addr_list[1] = NULL; @@ -467,24 +614,30 @@ open_inet(class, h, serv, type) servport = FIX2UINT(serv); goto setup_servent; } - Check_Type(serv, T_STRING); - servent = getservbyname(RSTRING(serv)->ptr, "tcp"); + servent = getservbyname(STR2CSTR(serv), "tcp"); if (servent == NULL) { - servport = strtoul(RSTRING(serv)->ptr, 0, 0); - if (servport == -1) { - Raise(eSocket, "no such servce %s", RSTRING(serv)->ptr); + char *s = STR2CSTR(serv); + char *end; + + servport = strtoul(s, &end, 0); + if (*end != '\0') { + rb_raise(rb_eSocket, "no such servce %s", s); } setup_servent: _servent.s_port = htons(servport); _servent.s_proto = "tcp"; servent = &_servent; } +#ifdef __BEOS__ + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +#else protoent = getprotobyname(servent->s_proto); if (protoent == NULL) { - Raise(eSocket, "no such proto %s", servent->s_proto); + rb_raise(rb_eSocket, "no such proto %s", servent->s_proto); } fd = socket(AF_INET, SOCK_STREAM, protoent->p_proto); +#endif memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; @@ -505,9 +658,9 @@ open_inet(class, h, serv, type) syscall = "bind(2)"; } else { -#if defined(THREAD) && defined(HAVE_FCNTL) +#if defined(USE_THREAD) && defined(HAVE_FCNTL) status = thread_connect(fd, (struct sockaddr*)&sockaddr, - sizeof(sockaddr), type); + sizeof(sockaddr), type); #else #ifdef SOCKS if (type == INET_SOCKS) { @@ -523,7 +676,7 @@ open_inet(class, h, serv, type) } if (status < 0) { - close (fd); + close(fd); rb_sys_fail(syscall); } if (type == INET_SERVER) listen(fd, 5); @@ -558,6 +711,49 @@ socks_s_open(class, host, serv) #endif static VALUE +tcp_s_gethostbyname(obj, host) + VALUE obj, host; +{ + struct sockaddr_in addr; + struct hostent *h; + char **pch; + VALUE ary, names; + + setipaddr(STR2CSTR(host), &addr); + h = gethostbyaddr((char *)&addr.sin_addr, + sizeof(addr.sin_addr), + AF_INET); + + if (h == NULL) { +#ifdef HAVE_HSTRERROR + extern int h_errno; + rb_raise(rb_eSocket, (char *)hstrerror(h_errno)); +#else + rb_raise(rb_eSocket, "host not found"); +#endif + } + ary = rb_ary_new(); + rb_ary_push(ary, rb_str_new2(h->h_name)); + names = rb_ary_new(); + rb_ary_push(ary, names); + for (pch = h->h_aliases; *pch; pch++) { + rb_ary_push(names, rb_str_new2(*pch)); + } + rb_ary_push(ary, NUM2INT(h->h_addrtype)); +#ifdef h_addr + for (pch = h->h_addr_list; *pch; pch++) { + memcpy((char *) &addr.sin_addr, *pch, h->h_length); + rb_ary_push(ary, mkipaddr(addr.sin_addr.s_addr)); + } +#else + memcpy((char *)&addr.sin_addr, h->h_addr, h->h_length); + rb_ary_push(ary, mkipaddr(addr.sin_addr.s_addr)); +#endif + + return ary; +} + +static VALUE tcp_svr_s_open(argc, argv, class) int argc; VALUE *argv; @@ -581,8 +777,8 @@ s_accept(class, fd, sockaddr, len) int fd2; retry: -#ifdef THREAD - thread_wait_fd(fd); +#ifdef USE_THREAD + rb_thread_wait_fd(fd); #endif TRAP_BEG; fd2 = accept(fd, sockaddr, len); @@ -594,8 +790,8 @@ s_accept(class, fd, sockaddr, len) #if EAGAIN != EWOULDBLOCK case EAGAIN: #endif -#ifdef THREAD - thread_schedule(); +#ifdef USE_THREAD + rb_thread_schedule(); #endif goto retry; } @@ -614,7 +810,7 @@ tcp_accept(sock) GetOpenFile(sock, fptr); fromlen = sizeof(struct sockaddr_in); - return s_accept(cTCPsocket, fileno(fptr->f), + return s_accept(rb_cTCPsocket, fileno(fptr->f), (struct sockaddr*)&from, &fromlen); } @@ -670,81 +866,6 @@ open_unix(class, path, server) } #endif -static void -setipaddr(name, addr) - char *name; - struct sockaddr_in *addr; -{ - int d1, d2, d3, d4; - char ch; - struct hostent *hp; - long x; - - if (name[0] == 0) { - addr->sin_addr.s_addr = INADDR_ANY; - } - else if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) { - addr->sin_addr.s_addr = INADDR_BROADCAST; - } - else if (sscanf(name, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 && - 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 && - 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) { - addr->sin_addr.s_addr = htonl( - ((long) d1 << 24) | ((long) d2 << 16) | - ((long) d3 << 8) | ((long) d4 << 0)); - } - else { - hp = gethostbyname(name); - if (!hp) { -#ifdef HAVE_HSTRERROR - extern int h_errno; - Raise(eSocket, (char *)hstrerror(h_errno)); -#else - Raise(eSocket, "host not found"); -#endif - } - memcpy((char *) &addr->sin_addr, hp->h_addr, hp->h_length); - } -} - -static VALUE -mkipaddr(x) - unsigned long x; -{ - char buf[16]; - - x = ntohl(x); - sprintf(buf, "%d.%d.%d.%d", - (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, - (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); - return str_new2(buf); -} - -static VALUE -ipaddr(sockaddr) - struct sockaddr_in *sockaddr; -{ - VALUE family, port, addr1, addr2; - VALUE ary; - struct hostent *hostent; - - family = str_new2("AF_INET"); - hostent = gethostbyaddr((char*)&sockaddr->sin_addr.s_addr, - sizeof(sockaddr->sin_addr), - AF_INET); - addr1 = 0; - if (hostent) { - addr1 = str_new2(hostent->h_name); - } - addr2 = mkipaddr(sockaddr->sin_addr.s_addr); - if (!addr1) addr1 = addr2; - - port = INT2FIX(ntohs(sockaddr->sin_port)); - ary = ary_new3(4, family, port, addr1, addr2); - - return ary; -} - static VALUE ip_addr(sock) VALUE sock; @@ -781,13 +902,12 @@ ip_s_getaddress(obj, host) { struct sockaddr_in addr; - if (obj_is_kind_of(host, cInteger)) { + if (rb_obj_is_kind_of(host, rb_cInteger)) { int i = NUM2INT(host); addr.sin_addr.s_addr = htonl(i); } else { - Check_Type(host, T_STRING); - setipaddr(RSTRING(host)->ptr, &addr); + setipaddr(STR2CSTR(host), &addr); } return mkipaddr(addr.sin_addr.s_addr); @@ -805,37 +925,36 @@ udp_addrsetup(host, port, addr) VALUE host, port; struct sockaddr_in *addr; { - struct hostent *hostent; - memset(addr, 0, sizeof(struct sockaddr_in)); addr->sin_family = AF_INET; if (NIL_P(host)) { addr->sin_addr.s_addr = INADDR_ANY; } - else if (obj_is_kind_of(host, cInteger)) { + else if (rb_obj_is_kind_of(host, rb_cInteger)) { int i = NUM2INT(host); addr->sin_addr.s_addr = htonl(i); } else { - Check_Type(host, T_STRING); - setipaddr(RSTRING(host)->ptr, addr); + setipaddr(STR2CSTR(host), addr); } if (FIXNUM_P(port)) { - addr->sin_port = FIX2INT(port); + addr->sin_port = htons(FIX2INT(port)); } else { struct servent *servent; - Check_Type(port, T_STRING); - servent = getservbyname(RSTRING(port)->ptr, "udp"); + servent = getservbyname(STR2CSTR(port), "udp"); if (servent) { addr->sin_port = servent->s_port; } else { - int port = strtoul(RSTRING(port)->ptr, 0, 0); + char *s = STR2CSTR(port); + char *end; + int portno; - if (port == -1) { - Raise(eSocket, "no such servce %s", RSTRING(port)->ptr); + portno = strtoul(s, &end, 0); + if (*end != '\0') { + rb_raise(rb_eSocket, "no such servce %s", s); } addr->sin_port = htons(port); } @@ -859,8 +978,8 @@ udp_connect(sock, host, port) #if EAGAIN != EWOULDBLOCK case EAGAIN: #endif -#ifdef THREAD - thread_schedule(); +#ifdef USE_THREAD + rb_thread_schedule(); #endif goto retry; } @@ -896,19 +1015,21 @@ udp_send(argc, argv, sock) OpenFile *fptr; FILE *f; int n; + char *m; + int mlen; if (argc == 2) { return bsock_send(argc, argv, sock); } rb_scan_args(argc, argv, "4", &mesg, &flags, &host, &port); - Check_Type(mesg, T_STRING); udp_addrsetup(host, port, &addr); GetOpenFile(sock, fptr); - f = fptr->f2?fptr->f2:fptr->f; + f = GetWriteFile(fptr); + m = rb_str2cstr(mesg, &mlen); retry: - n = sendto(fileno(f), RSTRING(mesg)->ptr, RSTRING(mesg)->len, - NUM2INT(flags), (struct sockaddr*)&addr, sizeof(addr)); + n = sendto(fileno(f), m, mlen, NUM2INT(flags), + (struct sockaddr*)&addr, sizeof(addr)); if (n < 0) { switch (errno) { case EINTR: @@ -916,8 +1037,8 @@ udp_send(argc, argv, sock) #if EAGAIN != EWOULDBLOCK case EAGAIN: #endif -#ifdef THREAD - thread_schedule(); +#ifdef USE_THREAD + rb_thread_schedule(); #endif goto retry; } @@ -957,14 +1078,14 @@ unix_path(sock) rb_sys_fail(0); fptr->path = strdup(addr.sun_path); } - return str_new2(fptr->path); + return rb_str_new2(fptr->path); } static VALUE -unix_svr_s_open(class, path) - VALUE class, path; +unix_svr_s_open(sock, path) + VALUE sock, path; { - return open_unix(class, path, 1); + return open_unix(sock, path, 1); } static VALUE @@ -986,7 +1107,7 @@ unix_accept(sock) GetOpenFile(sock, fptr); fromlen = sizeof(struct sockaddr_un); - return s_accept(cUNIXsocket, fileno(fptr->f), + return s_accept(rb_cUNIXsocket, fileno(fptr->f), (struct sockaddr*)&from, &fromlen); } @@ -994,7 +1115,7 @@ static VALUE unixaddr(sockaddr) struct sockaddr_un *sockaddr; { - return assoc_new(str_new2("AF_UNIX"),str_new2(sockaddr->sun_path)); + return rb_assoc_new(rb_str_new2("AF_UNIX"),rb_str_new2(sockaddr->sun_path)); } static VALUE @@ -1055,8 +1176,10 @@ setup_domain_and_type(domain, dv, type, tv) else if (strcmp(ptr, "AF_IMPLINK") == 0) *dv = AF_IMPLINK; #endif +#ifdef PF_INET else if (strcmp(ptr, "PF_INET") == 0) *dv = PF_INET; +#endif #ifdef PF_UNIX else if (strcmp(ptr, "PF_UNIX") == 0) *dv = PF_UNIX; @@ -1076,7 +1199,7 @@ setup_domain_and_type(domain, dv, type, tv) *dv = PF_IPX; #endif else - Raise(eSocket, "Unknown socket domain %s", ptr); + rb_raise(rb_eSocket, "Unknown socket domain %s", ptr); } else { *dv = NUM2INT(domain); @@ -1104,7 +1227,7 @@ setup_domain_and_type(domain, dv, type, tv) *tv = SOCK_PACKET; #endif else - Raise(eSocket, "Unknown socket type %s", ptr); + rb_raise(rb_eSocket, "Unknown socket type %s", ptr); } else { *tv = NUM2INT(type); @@ -1121,6 +1244,7 @@ sock_s_open(class, domain, type, protocol) setup_domain_and_type(domain, &d, type, &t); fd = socket(d, t, NUM2INT(protocol)); if (fd < 0) rb_sys_fail("socket(2)"); + return sock_new(class, fd); } @@ -1135,15 +1259,14 @@ static VALUE sock_s_socketpair(class, domain, type, protocol) VALUE class, domain, type, protocol; { -#if !defined(__CYGWIN32__) && !defined(NT) - int fd; +#if !defined(NT) && !defined(__BEOS__) int d, t, sp[2]; setup_domain_and_type(domain, &d, type, &t); if (socketpair(d, t, NUM2INT(protocol), sp) < 0) rb_sys_fail("socketpair(2)"); - return assoc_new(sock_new(class, sp[0]), sock_new(class, sp[1])); + return rb_assoc_new(sock_new(class, sp[0]), sock_new(class, sp[1])); #else rb_notimplement(); #endif @@ -1156,7 +1279,7 @@ sock_connect(sock, addr) OpenFile *fptr; Check_Type(addr, T_STRING); - str_modify(addr); + rb_str_modify(addr); GetOpenFile(sock, fptr); retry: @@ -1167,8 +1290,8 @@ sock_connect(sock, addr) #if EAGAIN != EWOULDBLOCK case EAGAIN: #endif -#ifdef THREAD - thread_schedule(); +#ifdef USE_THREAD + rb_thread_schedule(); #endif goto retry; } @@ -1185,7 +1308,7 @@ sock_bind(sock, addr) OpenFile *fptr; Check_Type(addr, T_STRING); - str_modify(addr); + rb_str_modify(addr); GetOpenFile(sock, fptr); if (bind(fileno(fptr->f), (struct sockaddr*)RSTRING(addr)->ptr, RSTRING(addr)->len) < 0) @@ -1226,9 +1349,9 @@ sock_accept(sock) int len = sizeof buf; GetOpenFile(sock, fptr); - sock2 = s_accept(cSocket,fileno(fptr->f),(struct sockaddr*)buf,&len); + sock2 = s_accept(rb_cSocket,fileno(fptr->f),(struct sockaddr*)buf,&len); - return assoc_new(sock2, str_new(buf, len)); + return rb_assoc_new(sock2, rb_str_new(buf, len)); } #ifdef HAVE_GETHOSTNAME @@ -1242,7 +1365,7 @@ sock_gethostname(obj) rb_sys_fail("gethostname"); buf[sizeof buf - 1] = '\0'; - return str_new2(buf); + return rb_str_new2(buf); } #else #ifdef HAVE_UNAME @@ -1256,7 +1379,7 @@ sock_gethostname(obj) struct utsname un; uname(&un); - return str_new2(un.nodename); + return rb_str_new2(un.nodename); } #else static VALUE @@ -1272,32 +1395,31 @@ static VALUE mkhostent(h) struct hostent *h; { - struct sockaddr_in addr; char **pch; VALUE ary, names; if (h == NULL) { #ifdef HAVE_HSTRERROR extern int h_errno; - Raise(eSocket, (char *)hstrerror(h_errno)); + rb_raise(rb_eSocket, (char *)hstrerror(h_errno)); #else - Raise(eSocket, "host not found"); + rb_raise(rb_eSocket, "host not found"); #endif } - ary = ary_new(); - ary_push(ary, str_new2(h->h_name)); - names = ary_new(); - ary_push(ary, names); + ary = rb_ary_new(); + rb_ary_push(ary, rb_str_new2(h->h_name)); + names = rb_ary_new(); + rb_ary_push(ary, names); for (pch = h->h_aliases; *pch; pch++) { - ary_push(names, str_new2(*pch)); + rb_ary_push(names, rb_str_new2(*pch)); } - ary_push(ary, INT2FIX(h->h_length)); + rb_ary_push(ary, NUM2INT(h->h_addrtype)); #ifdef h_addr for (pch = h->h_addr_list; *pch; pch++) { - ary_push(ary, str_new(*pch, h->h_length)); + rb_ary_push(ary, rb_str_new(*pch, h->h_length)); } #else - ary_push(ary, str_new(h->h_addr, h->h_length)); + rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length)); #endif return ary; @@ -1310,13 +1432,12 @@ sock_s_gethostbyname(obj, host) struct sockaddr_in addr; struct hostent *h; - if (obj_is_kind_of(host, cInteger)) { + if (rb_obj_is_kind_of(host, rb_cInteger)) { int i = NUM2INT(host); addr.sin_addr.s_addr = htonl(i); } else { - Check_Type(host, T_STRING); - setipaddr(RSTRING(host)->ptr, &addr); + setipaddr(STR2CSTR(host), &addr); } h = gethostbyaddr((char *)&addr.sin_addr, sizeof(addr.sin_addr), @@ -1332,20 +1453,20 @@ sock_s_gethostbyaddr(argc, argv) { VALUE vaddr, vtype; int type; - - struct sockaddr_in *addr; + int alen; + char *addr; struct hostent *h; - rb_scan_args(argc, argv, "11", &addr, &type); - Check_Type(addr, T_STRING); - if (!NIL_P(type)) { + rb_scan_args(argc, argv, "11", &vaddr, &vtype); + addr = rb_str2cstr(vaddr, &alen); + if (!NIL_P(vtype)) { type = NUM2INT(vtype); } else { type = AF_INET; } - h = gethostbyaddr(RSTRING(addr)->ptr, RSTRING(addr)->len, type); + h = gethostbyaddr(addr, alen, type); return mkhostent(h); } @@ -1361,15 +1482,22 @@ sock_s_getservbyaname(argc, argv) int port; rb_scan_args(argc, argv, "11", &service, &protocol); - Check_Type(service, T_STRING); if (NIL_P(protocol)) proto = "tcp"; - else proto = RSTRING(protocol)->ptr; + else proto = STR2CSTR(protocol); - sp = getservbyname(RSTRING(service)->ptr, proto); - if (!sp) { - Raise(eSocket, "service/proto not found"); + sp = getservbyname(STR2CSTR(service), proto); + if (sp) { + port = ntohs(sp->s_port); + } + else { + char *s = STR2CSTR(service); + char *end; + + port = strtoul(s, &end, 0); + if (*end != '\0') { + rb_raise(rb_eSocket, "no such servce %s/%s", s, proto); + } } - port = ntohs(sp->s_port); return INT2FIX(port); } @@ -1377,225 +1505,240 @@ sock_s_getservbyaname(argc, argv) static VALUE mConst; static void -sock_define_const(name, value) +sock_rb_define_const(name, value) char *name; - INT value; + int value; { - rb_define_const(cSocket, name, INT2FIX(value)); + rb_define_const(rb_cSocket, name, INT2FIX(value)); rb_define_const(mConst, name, INT2FIX(value)); } Init_socket() { - eSocket = rb_define_class("SocketError", eException); - - cBasicSocket = rb_define_class("BasicSocket", cIO); - rb_undef_method(CLASS_OF(cBasicSocket), "new"); - rb_undef_method(CLASS_OF(cBasicSocket), "open"); - rb_define_method(cBasicSocket, "shutdown", bsock_shutdown, -1); - rb_define_method(cBasicSocket, "setsockopt", bsock_setsockopt, 3); - rb_define_method(cBasicSocket, "getsockopt", bsock_getsockopt, 2); - rb_define_method(cBasicSocket, "getsockname", bsock_getsockname, 0); - rb_define_method(cBasicSocket, "getpeername", bsock_getpeername, 0); - rb_define_method(cBasicSocket, "send", bsock_send, -1); - rb_define_method(cBasicSocket, "recv", bsock_recv, -1); - - cIPsocket = rb_define_class("IPsocket", cBasicSocket); - rb_define_method(cIPsocket, "addr", ip_addr, 0); - rb_define_method(cIPsocket, "peeraddr", ip_peeraddr, 0); - rb_define_singleton_method(cIPsocket, "getaddress", ip_s_getaddress, 1); - - cTCPsocket = rb_define_class("TCPsocket", cIPsocket); - rb_define_singleton_method(cTCPsocket, "open", tcp_s_open, 2); - rb_define_singleton_method(cTCPsocket, "new", tcp_s_open, 2); - rb_define_method(cTCPsocket, "recvfrom", tcp_recvfrom, -1); + rb_eSocket = rb_define_class("SocketError", rb_eStandardError); + + rb_cBasicSocket = rb_define_class("BasicSocket", rb_cIO); + rb_undef_method(CLASS_OF(rb_cBasicSocket), "new"); + rb_undef_method(CLASS_OF(rb_cBasicSocket), "open"); + rb_define_method(rb_cBasicSocket, "close_read", bsock_close_read, 0); + rb_define_method(rb_cBasicSocket, "close_write", bsock_close_write, 0); + rb_define_method(rb_cBasicSocket, "shutdown", bsock_shutdown, -1); + rb_define_method(rb_cBasicSocket, "setsockopt", bsock_setsockopt, 3); + rb_define_method(rb_cBasicSocket, "getsockopt", bsock_getsockopt, 2); + rb_define_method(rb_cBasicSocket, "getsockname", bsock_getsockname, 0); + rb_define_method(rb_cBasicSocket, "getpeername", bsock_getpeername, 0); + rb_define_method(rb_cBasicSocket, "send", bsock_send, -1); + rb_define_method(rb_cBasicSocket, "recv", bsock_recv, -1); + + rb_cIPsocket = rb_define_class("IPsocket", rb_cBasicSocket); + rb_define_method(rb_cIPsocket, "addr", ip_addr, 0); + rb_define_method(rb_cIPsocket, "peeraddr", ip_peeraddr, 0); + rb_define_singleton_method(rb_cIPsocket, "getaddress", ip_s_getaddress, 1); + + rb_cTCPsocket = rb_define_class("TCPsocket", rb_cIPsocket); + rb_define_singleton_method(rb_cTCPsocket, "open", tcp_s_open, 2); + rb_define_singleton_method(rb_cTCPsocket, "new", tcp_s_open, 2); + rb_define_singleton_method(rb_cTCPsocket, "gethostbyname", tcp_s_gethostbyname, 1); + rb_define_method(rb_cTCPsocket, "recvfrom", tcp_recvfrom, -1); #ifdef SOCKS - cSOCKSsocket = rb_define_class("SOCKSsocket", cTCPsocket); - rb_define_singleton_method(cSOCKSsocket, "open", socks_s_open, 2); - rb_define_singleton_method(cSOCKSsocket, "new", socks_s_open, 2); + rb_cSOCKSsocket = rb_define_class("SOCKSsocket", rb_cTCPsocket); + rb_define_singleton_method(rb_cSOCKSsocket, "open", socks_s_open, 2); + rb_define_singleton_method(rb_cSOCKSsocket, "new", socks_s_open, 2); #endif - cTCPserver = rb_define_class("TCPserver", cTCPsocket); - rb_define_singleton_method(cTCPserver, "open", tcp_svr_s_open, -1); - rb_define_singleton_method(cTCPserver, "new", tcp_svr_s_open, -1); - rb_define_method(cTCPserver, "accept", tcp_accept, 0); + rb_cTCPserver = rb_define_class("TCPserver", rb_cTCPsocket); + rb_define_singleton_method(rb_cTCPserver, "open", tcp_svr_s_open, -1); + rb_define_singleton_method(rb_cTCPserver, "new", tcp_svr_s_open, -1); + rb_define_method(rb_cTCPserver, "accept", tcp_accept, 0); - cUDPsocket = rb_define_class("UDPsocket", cIPsocket); - rb_define_singleton_method(cUDPsocket, "open", udp_s_open, 0); - rb_define_singleton_method(cUDPsocket, "new", udp_s_open, 0); - rb_define_method(cUDPsocket, "connect", udp_connect, 2); - rb_define_method(cUDPsocket, "bind", udp_bind, 2); - rb_define_method(cUDPsocket, "send", udp_send, -1); - rb_define_method(cUDPsocket, "recvfrom", udp_recvfrom, -1); + rb_cUDPsocket = rb_define_class("UDPsocket", rb_cIPsocket); + rb_define_singleton_method(rb_cUDPsocket, "open", udp_s_open, 0); + rb_define_singleton_method(rb_cUDPsocket, "new", udp_s_open, 0); + rb_define_method(rb_cUDPsocket, "connect", udp_connect, 2); + rb_define_method(rb_cUDPsocket, "bind", udp_bind, 2); + rb_define_method(rb_cUDPsocket, "send", udp_send, -1); + rb_define_method(rb_cUDPsocket, "recvfrom", udp_recvfrom, -1); #ifdef HAVE_SYS_UN_H - cUNIXsocket = rb_define_class("UNIXsocket", cBasicSocket); - rb_define_singleton_method(cUNIXsocket, "open", unix_s_sock_open, 1); - rb_define_singleton_method(cUNIXsocket, "new", unix_s_sock_open, 1); - rb_define_method(cUNIXsocket, "path", unix_path, 0); - rb_define_method(cUNIXsocket, "addr", unix_addr, 0); - rb_define_method(cUNIXsocket, "peeraddr", unix_peeraddr, 0); - rb_define_method(cUNIXsocket, "recvfrom", unix_recvfrom, -1); - - cUNIXserver = rb_define_class("UNIXserver", cUNIXsocket); - rb_define_singleton_method(cUNIXserver, "open", unix_svr_s_open, 1); - rb_define_singleton_method(cUNIXserver, "new", unix_svr_s_open, 1); - rb_define_method(cUNIXserver, "accept", unix_accept, 0); -#endif - - cSocket = rb_define_class("Socket", cBasicSocket); - rb_define_singleton_method(cSocket, "open", sock_s_open, 3); - rb_define_singleton_method(cSocket, "new", sock_s_open, 3); - rb_define_singleton_method(cSocket, "for_fd", sock_s_for_fd, 1); - - rb_define_method(cSocket, "connect", sock_connect, 1); - rb_define_method(cSocket, "bind", sock_bind, 1); - rb_define_method(cSocket, "listen", sock_listen, 1); - rb_define_method(cSocket, "accept", sock_accept, 0); - - rb_define_method(cSocket, "recvfrom", sock_recvfrom, -1); - - rb_define_singleton_method(cSocket, "socketpair", sock_s_socketpair, 3); - rb_define_singleton_method(cSocket, "pair", sock_s_socketpair, 3); - rb_define_singleton_method(cSocket, "gethostname", sock_gethostname, 0); - rb_define_singleton_method(cSocket, "gethostbyname", sock_s_gethostbyname, 1); - rb_define_singleton_method(cSocket, "gethostbyaddr", sock_s_gethostbyaddr, -1); - rb_define_singleton_method(cSocket, "getservbyname", sock_s_getservbyaname, -1); + rb_cUNIXsocket = rb_define_class("UNIXsocket", rb_cBasicSocket); + rb_define_singleton_method(rb_cUNIXsocket, "open", unix_s_sock_open, 1); + rb_define_singleton_method(rb_cUNIXsocket, "new", unix_s_sock_open, 1); + rb_define_method(rb_cUNIXsocket, "path", unix_path, 0); + rb_define_method(rb_cUNIXsocket, "addr", unix_addr, 0); + rb_define_method(rb_cUNIXsocket, "peeraddr", unix_peeraddr, 0); + rb_define_method(rb_cUNIXsocket, "recvfrom", unix_recvfrom, -1); + + rb_cUNIXserver = rb_define_class("UNIXserver", rb_cUNIXsocket); + rb_define_singleton_method(rb_cUNIXserver, "open", unix_svr_s_open, 1); + rb_define_singleton_method(rb_cUNIXserver, "new", unix_svr_s_open, 1); + rb_define_method(rb_cUNIXserver, "accept", unix_accept, 0); +#endif + + rb_cSocket = rb_define_class("Socket", rb_cBasicSocket); + rb_define_singleton_method(rb_cSocket, "open", sock_s_open, 3); + rb_define_singleton_method(rb_cSocket, "new", sock_s_open, 3); + rb_define_singleton_method(rb_cSocket, "for_fd", sock_s_for_fd, 1); + + rb_define_method(rb_cSocket, "connect", sock_connect, 1); + rb_define_method(rb_cSocket, "bind", sock_bind, 1); + rb_define_method(rb_cSocket, "listen", sock_listen, 1); + rb_define_method(rb_cSocket, "accept", sock_accept, 0); + + rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1); + + rb_define_singleton_method(rb_cSocket, "socketpair", sock_s_socketpair, 3); + rb_define_singleton_method(rb_cSocket, "pair", sock_s_socketpair, 3); + rb_define_singleton_method(rb_cSocket, "gethostname", sock_gethostname, 0); + rb_define_singleton_method(rb_cSocket, "gethostbyname", sock_s_gethostbyname, 1); + rb_define_singleton_method(rb_cSocket, "gethostbyaddr", sock_s_gethostbyaddr, -1); + rb_define_singleton_method(rb_cSocket, "getservbyname", sock_s_getservbyaname, -1); /* constants */ - mConst = rb_define_module_under(cSocket, "Constants"); - sock_define_const("SOCK_STREAM", SOCK_STREAM); - sock_define_const("SOCK_DGRAM", SOCK_DGRAM); - sock_define_const("SOCK_RAW", SOCK_RAW); + mConst = rb_define_module_under(rb_cSocket, "Constants"); + sock_rb_define_const("SOCK_STREAM", SOCK_STREAM); + sock_rb_define_const("SOCK_DGRAM", SOCK_DGRAM); +#ifdef SOCK_RAW + sock_rb_define_const("SOCK_RAW", SOCK_RAW); +#endif #ifdef SOCK_RDM - sock_define_const("SOCK_RDM", SOCK_RDM); + sock_rb_define_const("SOCK_RDM", SOCK_RDM); #endif #ifdef SOCK_SEQPACKET - sock_define_const("SOCK_SEQPACKET", SOCK_SEQPACKET); + sock_rb_define_const("SOCK_SEQPACKET", SOCK_SEQPACKET); #endif #ifdef SOCK_PACKET - sock_define_const("SOCK_PACKET", SOCK_PACKET); + sock_rb_define_const("SOCK_PACKET", SOCK_PACKET); #endif - sock_define_const("AF_INET", AF_INET); - sock_define_const("PF_INET", PF_INET); + sock_rb_define_const("AF_INET", AF_INET); +#ifdef PF_INET + sock_rb_define_const("PF_INET", PF_INET); +#endif #ifdef AF_UNIX - sock_define_const("AF_UNIX", AF_UNIX); - sock_define_const("PF_UNIX", PF_UNIX); + sock_rb_define_const("AF_UNIX", AF_UNIX); + sock_rb_define_const("PF_UNIX", PF_UNIX); #endif #ifdef AF_AX25 - sock_define_const("AF_AX25", AF_AX25); - sock_define_const("PF_AX25", PF_AX25); + sock_rb_define_const("AF_AX25", AF_AX25); + sock_rb_define_const("PF_AX25", PF_AX25); #endif #ifdef AF_IPX - sock_define_const("AF_IPX", AF_IPX); - sock_define_const("PF_IPX", PF_IPX); + sock_rb_define_const("AF_IPX", AF_IPX); + sock_rb_define_const("PF_IPX", PF_IPX); #endif #ifdef AF_APPLETALK - sock_define_const("AF_APPLETALK", AF_APPLETALK); - sock_define_const("PF_APPLETALK", PF_APPLETALK); + sock_rb_define_const("AF_APPLETALK", AF_APPLETALK); + sock_rb_define_const("PF_APPLETALK", PF_APPLETALK); #endif - sock_define_const("MSG_OOB", MSG_OOB); - sock_define_const("MSG_PEEK", MSG_PEEK); - sock_define_const("MSG_DONTROUTE", MSG_DONTROUTE); + sock_rb_define_const("MSG_OOB", MSG_OOB); +#ifdef MSG_PEEK + sock_rb_define_const("MSG_PEEK", MSG_PEEK); +#endif +#ifdef MSG_DONTROUTE + sock_rb_define_const("MSG_DONTROUTE", MSG_DONTROUTE); +#endif - sock_define_const("SOL_SOCKET", SOL_SOCKET); + sock_rb_define_const("SOL_SOCKET", SOL_SOCKET); #ifdef SOL_IP - sock_define_const("SOL_IP", SOL_IP); + sock_rb_define_const("SOL_IP", SOL_IP); #endif #ifdef SOL_IPX - sock_define_const("SOL_IPX", SOL_IPX); + sock_rb_define_const("SOL_IPX", SOL_IPX); #endif #ifdef SOL_AX25 - sock_define_const("SOL_AX25", SOL_AX25); + sock_rb_define_const("SOL_AX25", SOL_AX25); #endif #ifdef SOL_ATALK - sock_define_const("SOL_ATALK", SOL_ATALK); + sock_rb_define_const("SOL_ATALK", SOL_ATALK); #endif #ifdef SOL_TCP - sock_define_const("SOL_TCP", SOL_TCP); + sock_rb_define_const("SOL_TCP", SOL_TCP); #endif #ifdef SOL_UDP - sock_define_const("SOL_UDP", SOL_UDP); + sock_rb_define_const("SOL_UDP", SOL_UDP); #endif #ifdef SO_DEBUG - sock_define_const("SO_DEBUG", SO_DEBUG); + sock_rb_define_const("SO_DEBUG", SO_DEBUG); #endif - sock_define_const("SO_REUSEADDR", SO_REUSEADDR); + sock_rb_define_const("SO_REUSEADDR", SO_REUSEADDR); #ifdef SO_TYPE - sock_define_const("SO_TYPE", SO_TYPE); + sock_rb_define_const("SO_TYPE", SO_TYPE); #endif #ifdef SO_ERROR - sock_define_const("SO_ERROR", SO_ERROR); + sock_rb_define_const("SO_ERROR", SO_ERROR); #endif #ifdef SO_DONTROUTE - sock_define_const("SO_DONTROUTE", SO_DONTROUTE); + sock_rb_define_const("SO_DONTROUTE", SO_DONTROUTE); #endif #ifdef SO_BROADCAST - sock_define_const("SO_BROADCAST", SO_BROADCAST); + sock_rb_define_const("SO_BROADCAST", SO_BROADCAST); #endif #ifdef SO_SNDBUF - sock_define_const("SO_SNDBUF", SO_SNDBUF); + sock_rb_define_const("SO_SNDBUF", SO_SNDBUF); #endif #ifdef SO_RCVBUF - sock_define_const("SO_RCVBUF", SO_RCVBUF); + sock_rb_define_const("SO_RCVBUF", SO_RCVBUF); +#endif +#ifdef SO_KEEPALIVE + sock_rb_define_const("SO_KEEPALIVE", SO_KEEPALIVE); #endif - sock_define_const("SO_KEEPALIVE", SO_KEEPALIVE); #ifdef SO_OOBINLINE - sock_define_const("SO_OOBINLINE", SO_OOBINLINE); + sock_rb_define_const("SO_OOBINLINE", SO_OOBINLINE); #endif #ifdef SO_NO_CHECK - sock_define_const("SO_NO_CHECK", SO_NO_CHECK); + sock_rb_define_const("SO_NO_CHECK", SO_NO_CHECK); #endif #ifdef SO_PRIORITY - sock_define_const("SO_PRIORITY", SO_PRIORITY); + sock_rb_define_const("SO_PRIORITY", SO_PRIORITY); +#endif +#ifdef SO_LINGER + sock_rb_define_const("SO_LINGER", SO_LINGER); #endif - sock_define_const("SO_LINGER", SO_LINGER); #ifdef SOPRI_INTERACTIVE - sock_define_const("SOPRI_INTERACTIVE", SOPRI_INTERACTIVE); + sock_rb_define_const("SOPRI_INTERACTIVE", SOPRI_INTERACTIVE); #endif #ifdef SOPRI_NORMAL - sock_define_const("SOPRI_NORMAL", SOPRI_NORMAL); + sock_rb_define_const("SOPRI_NORMAL", SOPRI_NORMAL); #endif #ifdef SOPRI_BACKGROUND - sock_define_const("SOPRI_BACKGROUND", SOPRI_BACKGROUND); + sock_rb_define_const("SOPRI_BACKGROUND", SOPRI_BACKGROUND); #endif #ifdef IP_MULTICAST_IF - sock_define_const("IP_MULTICAST_IF", IP_MULTICAST_IF); + sock_rb_define_const("IP_MULTICAST_IF", IP_MULTICAST_IF); #endif #ifdef IP_MULTICAST_TTL - sock_define_const("IP_MULTICAST_TTL", IP_MULTICAST_TTL); + sock_rb_define_const("IP_MULTICAST_TTL", IP_MULTICAST_TTL); #endif #ifdef IP_MULTICAST_LOOP - sock_define_const("IP_MULTICAST_LOOP", IP_MULTICAST_LOOP); + sock_rb_define_const("IP_MULTICAST_LOOP", IP_MULTICAST_LOOP); #endif #ifdef IP_ADD_MEMBERSHIP - sock_define_const("IP_ADD_MEMBERSHIP", IP_ADD_MEMBERSHIP); + sock_rb_define_const("IP_ADD_MEMBERSHIP", IP_ADD_MEMBERSHIP); #endif #ifdef IP_DEFAULT_MULTICAST_TTL - sock_define_const("IP_DEFAULT_MULTICAST_TTL", IP_DEFAULT_MULTICAST_TTL); + sock_rb_define_const("IP_DEFAULT_MULTICAST_TTL", IP_DEFAULT_MULTICAST_TTL); #endif #ifdef IP_DEFAULT_MULTICAST_LOOP - sock_define_const("IP_DEFAULT_MULTICAST_LOOP", IP_DEFAULT_MULTICAST_LOOP); + sock_rb_define_const("IP_DEFAULT_MULTICAST_LOOP", IP_DEFAULT_MULTICAST_LOOP); #endif #ifdef IP_MAX_MEMBERSHIPS - sock_define_const("IP_MAX_MEMBERSHIPS", IP_MAX_MEMBERSHIPS); + sock_rb_define_const("IP_MAX_MEMBERSHIPS", IP_MAX_MEMBERSHIPS); #endif #ifdef IPX_TYPE - sock_define_const("IPX_TYPE", IPX_TYPE); + sock_rb_define_const("IPX_TYPE", IPX_TYPE); #endif #ifdef TCP_NODELAY - sock_define_const("TCP_NODELAY", TCP_NODELAY); + sock_rb_define_const("TCP_NODELAY", TCP_NODELAY); #endif #ifdef TCP_MAXSEG - sock_define_const("TCP_MAXSEG", TCP_MAXSEG); + sock_rb_define_const("TCP_MAXSEG", TCP_MAXSEG); #endif } diff --git a/ext/tcltklib/extconf.rb b/ext/tcltklib/extconf.rb index 26e7fe7b09..e34e549ca0 100644 --- a/ext/tcltklib/extconf.rb +++ b/ext/tcltklib/extconf.rb @@ -1,19 +1,27 @@ # extconf.rb for tcltklib +require 'mkmf' + +have_library("nsl", "t_open") have_library("socket", "socket") -have_library("nsl", "gethostbyname") +have_library("dl", "dlopen") +have_library("m", "log") -def search_file(var, include, *path) +$includes = [] +def search_header(include, *path) pwd = Dir.getwd begin - for i in path.reverse! + for i in path.sort!.reverse! dir = Dir[i] - for path in dir + for path in dir.sort!.reverse! + next unless File.directory? path Dir.chdir path files = Dir[include] if files.size > 0 - var << path - return files.pop + unless $includes.include? path + $includes << path + end + return end end end @@ -22,58 +30,56 @@ def search_file(var, include, *path) end end -$includes = [] -search_file($includes, - "tcl.h", - "/usr/include/tcl*", - "/usr/include", - "/usr/local/include/tcl*", - "/usr/local/include") -search_file($includes, - "tk.h", - "/usr/include/tk*", - "/usr/include", - "/usr/local/include/tk*", - "/usr/local/include") -search_file($includes, - "X11/Xlib.h", - "/usr/include", - "/usr/X11*/include", - "/usr/include", - "/usr/X11*/include") +search_header("tcl.h", + "/usr/include/tcl{,8*,7*}", + "/usr/include", + "/usr/local/include/tcl{,8*,7*}", + "/usr/local/include") +search_header("tk.h", + "/usr/include/tk{,8*,4*}", + "/usr/include", + "/usr/local/include/tk{,8*,4*}", + "/usr/local/include") +search_header("X11/Xlib.h", + "/usr/include/X11*", + "/usr/include", + "/usr/openwin/include", + "/usr/X11*/include") -$CFLAGS = "-Wall " + $includes.collect{|path| "-I" + path}.join(" ") +$CFLAGS = $includes.collect{|path| "-I" + path}.join(" ") $libraries = [] -tcllibfile = search_file($libraries, - "libtcl{,7*,8*}.{a,so}", - "/usr/lib", - "/usr/local/lib") -if tcllibfile - tcllibfile.sub!(/^lib/, '') - tcllibfile.sub!(/\.(a|so)$/, '') -end -tklibfile = search_file($libraries, - "libtk{,4*,8*}.{a,so}", - "/usr/lib", - "/usr/local/lib") -if tklibfile - tklibfile.sub!(/^lib/, '') - tklibfile.sub!(/\.(a|so)$/, '') +def search_lib(file, func, *path) + for i in path.reverse! + dir = Dir[i] + for path in dir.sort!.reverse! + $LDFLAGS = $libraries.collect{|p| "-L" + p}.join(" ") + " -L" + path + files = Dir[path+"/"+file] + if files.size > 0 + for lib in files.sort!.reverse! + lib = File::basename(lib) + lib.sub!(/^lib/, '') + lib.sub!(/\.(a|so)$/, '') + if have_library(lib, func) + unless $libraries.include? path + $libraries << path + end + return true + end + end + end + end + end + return false; end -search_file($libraries, - "libX11.{a,so}", - "/usr/lib", - "/usr/X11*/lib") -$LDFLAGS = $libraries.collect{|path| "-L" + path}.join(" ") - -have_library("dl", "dlopen") -if have_header("tcl.h") && - have_header("tk.h") && - have_library("X11", "XOpenDisplay") && - have_library("m", "log") && - have_library(tcllibfile, "Tcl_FindExecutable") && - have_library(tklibfile, "Tk_Init") +if have_header("tcl.h") && have_header("tk.h") && + search_lib("libX11.{so,a}", "XOpenDisplay", + "/usr/lib", "/usr/openwin/lib", "/usr/X11*/lib") && + search_lib("libtcl{8*,7*,}.{so,a}", "Tcl_FindExecutable", + "/usr/lib", "/usr/local/lib") && + search_lib("libtk{8*,4*,}.{so,a}", "Tk_Init", + "/usr/lib", "/usr/local/lib") + $LDFLAGS = $libraries.collect{|path| "-L" + path}.join(" ") create_makefile("tcltklib") end diff --git a/ext/tcltklib/lib/tcltk.rb b/ext/tcltklib/lib/tcltk.rb index 81d01f930d..54a00e8f3c 100644 --- a/ext/tcltklib/lib/tcltk.rb +++ b/ext/tcltklib/lib/tcltk.rb @@ -1,48 +1,44 @@ # tof -#### tcltk ¥é¥¤¥Ö¥é¥ê +#### tcltk library, more direct manipulation of tcl/tk #### Sep. 5, 1997 Y. Shigehiro require "tcltklib" ################ -# module TclTk: tcl/tk ¤Î¥é¥¤¥Ö¥é¥êÁ´ÂΤÇɬÍפˤʤë¤â¤Î¤ò½¸¤á¤¿¤â¤Î -# (¼ç¤Ë, ̾Á°¶õ´Ö¤ÎÅÀ¤«¤é module ¤Ë¤¹¤ë»È¤¦.) +# module TclTk: collection of tcl/tk utilities (supplies namespace.) module TclTk - # ñ¤Ë¤³¤³¤Ë½ñ¤±¤ÐºÇ½é¤Ë 1 Åټ¹Ԥµ¤ì¤ë¤Î¤«?? - - # À¸À®¤·¤¿°ì°Õ¤Ê̾Á°¤òÊÝ»ý¤·¤Æ¤ª¤¯Ï¢ÁÛÇÛÎó¤ò½é´ü²½¤¹¤ë. + # initialize Hash to hold unique symbols and such @namecnt = {} - # ¥³¡¼¥ë¥Ð¥Ã¥¯¤òÊÝ»ý¤·¤Æ¤ª¤¯Ï¢ÁÛÇÛÎó¤ò½é´ü²½¤¹¤ë. + # initialize Hash to hold callbacks @callback = {} end -# TclTk.mainloop(): TclTkLib.mainloop() ¤ò¸Æ¤Ö. +# TclTk.mainloop(): call TclTkLib.mainloop() def TclTk.mainloop() print("mainloop: start\n") if $DEBUG TclTkLib.mainloop() print("mainloop: end\n") if $DEBUG end -# TclTk.deletecallbackkey(ca): ¥³¡¼¥ë¥Ð¥Ã¥¯¤ò TclTk module ¤«¤é¼è¤ê½ü¤¯. -# tcl/tk ¥¤¥ó¥¿¥×¥ê¥¿¤Ë¤ª¤¤¤Æ¥³¡¼¥ë¥Ð¥Ã¥¯¤¬¼è¤ê¾Ã¤µ¤ì¤ë¤ï¤±¤Ç¤Ï¤Ê¤¤. -# ¤³¤ì¤ò¤·¤Ê¤¤¤È, ºÇ¸å¤Ë TclTkInterpreter ¤¬ GC ¤Ç¤¤Ê¤¤. -# (GC ¤·¤¿¤¯¤Ê¤±¤ì¤Ð, Ê̤Ë, ¤³¤ì¤ò¤·¤Ê¤¯¤Æ¤âÎɤ¤.) -# ca: ¥³¡¼¥ë¥Ð¥Ã¥¯(TclTkCallback) +# TclTk.deletecallbackkey(ca): remove callback from TclTk module +# this does not remove callbacks from tcl/tk interpreter +# without calling this method, TclTkInterpreter will not be GCed +# ca: callback(TclTkCallback) def TclTk.deletecallbackkey(ca) print("deletecallbackkey: ", ca.to_s(), "\n") if $DEBUG @callback.delete(ca.to_s) end -# TclTk.dcb(ca, wid, W): ÇÛÎó¤ËÆþ¤Ã¤Æ¤¤¤ëÊ£¿ô¤Î¥³¡¼¥ë¥Ð¥Ã¥¯¤ËÂФ·¤Æ -# TclTk.deletecallbackkey() ¤ò¸Æ¤Ö. -# ¥È¥Ã¥×¥ì¥Ù¥ë¤Î <Destroy> ¥¤¥Ù¥ó¥È¤Î¥³¡¼¥ë¥Ð¥Ã¥¯¤È¤·¤Æ¸Æ¤Ö¤¿¤á¤Î¤â¤Î. -# ca: ¥³¡¼¥ë¥Ð¥Ã¥¯(TclTkCallback) ¤Î Array -# wid: ¥È¥Ã¥×¥ì¥Ù¥ë¤Î¥¦¥£¥¸¥§¥Ã¥È(TclTkWidget) -# w: ¥³¡¼¥ë¥Ð¥Ã¥¯¤Ë %W ¤ÇÍ¿¤¨¤é¤ì¤ë, ¥¦¥¤¥ó¥É¥¦¤Ë´Ø¤¹¤ë¥Ñ¥é¥á¡¼¥¿(String) +# TclTk.dcb(ca, wid, W): call TclTk.deletecallbackkey() for each callbacks +# in an array. +# this is for callback for top-level <Destroy> +# ca: array of callbacks(TclTkCallback) +# wid: top-level widget(TclTkWidget) +# w: information about window given by %W(String) def TclTk.dcb(ca, wid, w) if wid.to_s() == w ca.each{|i| @@ -51,33 +47,33 @@ def TclTk.dcb(ca, wid, w) end end -# TclTk._addcallback(ca): ¥³¡¼¥ë¥Ð¥Ã¥¯¤òÅÐÏ¿¤¹¤ë. -# ca: ¥³¡¼¥ë¥Ð¥Ã¥¯(TclTkCallback) +# TclTk._addcallback(ca): register callback +# ca: callback(TclTkCallback) def TclTk._addcallback(ca) print("_addcallback: ", ca.to_s(), "\n") if $DEBUG @callback[ca.to_s()] = ca end -# TclTk._callcallback(key, arg): ÅÐÏ¿¤·¤¿¥³¡¼¥ë¥Ð¥Ã¥¯¤ò¸Æ¤Ó½Ð¤¹. -# key: ¥³¡¼¥ë¥Ð¥Ã¥¯¤òÁªÂò¤¹¤ë¥¡¼ (TclTkCallback ¤¬ to_s() ¤ÇÊÖ¤¹ÃÍ) -# arg: tcl/tk ¥¤¥ó¥¿¥×¥ê¥¿¤«¤é¤Î¥Ñ¥é¥á¡¼¥¿ +# TclTk._callcallback(key, arg): invoke registered callback +# key: key to select callback (to_s value of the TclTkCallback) +# arg: parameter from tcl/tk interpreter def TclTk._callcallback(key, arg) print("_callcallback: ", @callback[key].inspect, "\n") if $DEBUG @callback[key]._call(arg) - # ¥³¡¼¥ë¥Ð¥Ã¥¯¤«¤é¤ÎÊÖ¤êÃͤϤɤ¦¤»¼Î¤Æ¤é¤ì¤ë. - # String ¤òÊÖ¤µ¤Ê¤¤¤È, rb_eval_string() ¤¬¥¨¥é¡¼¤Ë¤Ê¤ë. + # throw out callback value + # should return String to satisfy rb_eval_string() return "" end -# TclTk._newname(prefix): °ì°Õ¤Ê̾Á°(String)¤òÀ¸À®¤·¤ÆÊÖ¤¹. -# prefix: ̾Á°¤ÎÀÜÆ¬¸ì +# TclTk._newname(prefix): generate unique name(String) +# prefix: prefix of the unique name def TclTk._newname(prefix) - # À¸À®¤·¤¿Ì¾Á°¤Î¥«¥¦¥ó¥¿¤Ï @namecnt ¤ËÆþ¤Ã¤Æ¤¤¤ë¤Î¤Ç, Ä´¤Ù¤ë. + # generated name counter is stored in @namecnt if !@namecnt.key?(prefix) - # ½é¤á¤Æ»È¤¦ÀÜÆ¬¸ì¤Ê¤Î¤Ç½é´ü²½¤¹¤ë. + # first appearing prefix, initialize @namecnt[prefix] = 1 else - # »È¤Ã¤¿¤³¤È¤Î¤¢¤ëÀÜÆ¬¸ì¤Ê¤Î¤Ç, ¼¡¤Î̾Á°¤Ë¤¹¤ë. + # already appeared prefix, generate next name @namecnt[prefix] += 1 end return "#{prefix}#{@namecnt[prefix]}" @@ -85,51 +81,48 @@ end ################ -# class TclTkInterpreter: tcl/tk ¤Î¥¤¥ó¥¿¥×¥ê¥¿ +# class TclTkInterpreter: tcl/tk interpreter class TclTkInterpreter - # initialize(): ½é´ü²½. + # initialize(): def initialize() - # ¥¤¥ó¥¿¥×¥ê¥¿¤òÀ¸À®¤¹¤ë. + # generate interpreter object @ip = TclTkIp.new() - # ¥¤¥ó¥¿¥×¥ê¥¿¤Ë ruby_fmt ¥³¥Þ¥ó¥É¤òÄɲ乤ë. - # ruby_fmt ¥³¥Þ¥ó¥É¤È¤Ï, ¸å¤í¤Î°ú¿ô¤ò format ¥³¥Þ¥ó¥É¤Ç½èÍý¤·¤Æ - # ruby ¥³¥Þ¥ó¥É¤ËÅϤ¹¤â¤Î¤Ç¤¢¤ë. - # (¤Ê¤ª, ruby ¥³¥Þ¥ó¥É¤Ï, °ú¿ô¤ò 1 ¤Ä¤·¤«¤È¤ì¤Ê¤¤.) + # add ruby_fmt command to tcl interpreter + # ruby_fmt command format arguments by `format' and call `ruby' command + # (notice ruby command receives only one argument) if $DEBUG @ip._eval("proc ruby_fmt {fmt args} { puts \"ruby_fmt: $fmt $args\" ; ruby [format $fmt $args] }") else @ip._eval("proc ruby_fmt {fmt args} { ruby [format $fmt $args] }") end - # @ip._get_eval_string(*args): tcl/tk ¥¤¥ó¥¿¥×¥ê¥¿¤Çɾ²Á¤¹¤ë - # ʸ»úÎó(String)¤òÀ¸À®¤·¤ÆÊÖ¤¹. - # *args: tcl/tk ¤Çɾ²Á¤¹¤ë¥¹¥¯¥ê¥×¥È(¤ËÂбþ¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥ÈÎó) + # @ip._get_eval_string(*args): generate string to evaluate in tcl interpreter + # *args: script which is going to be evaluated under tcl/tk def @ip._get_eval_string(*args) argstr = "" args.each{|arg| argstr += " " if argstr != "" - # ¤â¤· to_eval() ¥á¥½¥Ã¥É¤¬ + # call to_eval if it is defined if (arg.respond_to?(:to_eval)) - # ÄêµÁ¤µ¤ì¤Æ¤¤¤ì¤Ð¤½¤ì¤ò¸Æ¤Ö. argstr += arg.to_eval() else - # ÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð to_s() ¤ò¸Æ¤Ö. + # call to_s unless defined argstr += arg.to_s() end } return argstr end - # @ip._eval_args(*args): tcl/tk ¥¤¥ó¥¿¥×¥ê¥¿¤Çɾ²Á¤·, - # ¤½¤Î·ë²Ì(String)¤òÊÖ¤¹. - # *args: tcl/tk ¤Çɾ²Á¤¹¤ë¥¹¥¯¥ê¥×¥È(¤ËÂбþ¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥ÈÎó) + # @ip._eval_args(*args): evaluate string under tcl/tk interpreter + # returns result string. + # *args: script which is going to be evaluated under tcl/tk def @ip._eval_args(*args) - # ¥¤¥ó¥¿¥×¥ê¥¿¤Çɾ²Á¤¹¤ëʸ»úÎó¤òµá¤á¤ë. + # calculate the string to eval in the interpreter argstr = _get_eval_string(*args) - # ¥¤¥ó¥¿¥×¥ê¥¿¤Çɾ²Á¤¹¤ë. + # evaluate under the interpreter print("_eval: \"", argstr, "\"") if $DEBUG res = _eval(argstr) if $DEBUG @@ -137,219 +130,205 @@ class TclTkInterpreter elsif _return_value() != 0 print(res, "\n") end - fail(%Q/can't eval "#{argstr}"/) if _return_value() != 0 + fail(%Q/can't eval "#{argstr}"/) if _return_value() != 0 #' return res end - # tcl/tk ¤Î¥³¥Þ¥ó¥É¤ËÂбþ¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·, Ï¢ÁÛÇÛÎó¤ËÆþ¤ì¤Æ¤ª¤¯. + # generate tcl/tk command object and register in the hash @commands = {} - # tcl/tk ¥¤¥ó¥¿¥×¥ê¥¿¤ËÅÐÏ¿¤µ¤ì¤Æ¤¤¤ë¤¹¤Ù¤Æ¤Î¥³¥Þ¥ó¥É¤ËÂФ·¤Æ, + # for all commands registered in tcl/tk interpreter: @ip._eval("info command").split(/ /).each{|comname| if comname =~ /^[.]/ - # ¥³¥Þ¥ó¥É¤¬¥¦¥£¥¸¥§¥Ã¥È(¤Î¥Ñ¥¹Ì¾)¤Î¾ì¹ç¤Ï - # TclTkWidget ¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤òºî¤Ã¤ÆÏ¢ÁÛÇÛÎó¤ËÆþ¤ì¤ë. + # if command is a widget (path), generate TclTkWidget, + # and register it in the hash @commands[comname] = TclTkWidget.new(@ip, comname) else - # ¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¤Ï - # TclTkCommand ¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤òºî¤Ã¤ÆÏ¢ÁÛÇÛÎó¤ËÆþ¤ì¤ë. + # otherwise, generate TclTkCommand @commands[comname] = TclTkCommand.new(@ip, comname) end } end - # commands(): tcl/tk ¤Î¥³¥Þ¥ó¥É¤ËÂбþ¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¤ò Hash ¤Ë - # Æþ¤ì¤¿¤â¤Î¤òÊÖ¤¹. + # commands(): returns hash of the tcl/tk commands def commands() return @commands end - # rootwidget(): ¥ë¡¼¥È¥¦¥£¥¸¥§¥Ã¥È(TclTkWidget)¤òÊÖ¤¹. + # rootwidget(): returns root widget(TclTkWidget) def rootwidget() return @commands["."] end - # _tcltkip(): @ip(TclTkIp) ¤òÊÖ¤¹. + # _tcltkip(): returns @ip(TclTkIp) def _tcltkip() return @ip end - # method_missing(id, *args): ̤ÄêµÁ¤Î¥á¥½¥Ã¥É¤Ï tcl/tk ¤Î¥³¥Þ¥ó¥É¤È¤ß¤Ê¤·¤Æ - # ¼Â¹Ô¤·, ¤½¤Î·ë²Ì(String)¤òÊÖ¤¹. - # id: ¥á¥½¥Ã¥É¤Î¥·¥ó¥Ü¥ë - # *args: ¥³¥Þ¥ó¥É¤Î°ú¿ô + # method_missing(id, *args): execute undefined method as tcl/tk command + # id: method symbol + # *args: method arguments def method_missing(id, *args) - # ¤â¤·, ¥á¥½¥Ã¥É¤Î tcl/tk ¥³¥Þ¥ó¥É¤¬ + # if command named by id registered, then execute it if @commands.key?(id.id2name) - # ¤¢¤ì¤Ð, ¼Â¹Ô¤·¤Æ·ë²Ì¤òÊÖ¤¹. return @commands[id.id2name].e(*args) else - # ̵¤±¤ì¤Ð¤â¤È¤â¤È¤Î½èÍý. + # otherwise, exception super end end end -# class TclTkObject: tcl/tk ¤Î¥ª¥Ö¥¸¥§¥¯¥È -# (´ðÄ쥯¥é¥¹¤È¤·¤Æ»È¤¦. -# tcltk ¥é¥¤¥Ö¥é¥ê¤ò»È¤¦¿Í¤¬ TclTkObject.new() ¤¹¤ë¤³¤È¤Ï¤Ê¤¤¤Ï¤º.) +# class TclTkObject: base class of the tcl/tk objects class TclTkObject - # initialize(ip, exp): ½é´ü²½. - # ip: ¥¤¥ó¥¿¥×¥ê¥¿(TclTkIp) - # exp: tcl/tk ¤Ç¤Îɽ¸½·Á + # initialize(ip, exp): + # ip: interpreter(TclTkIp) + # exp: tcl/tk representation def initialize(ip, exp) fail("type is not TclTkIp") if !ip.kind_of?(TclTkIp) @ip = ip @exp = exp end - # to_s(): tcl/tk ¤Ç¤Îɽ¸½·Á(String)¤òÊÖ¤¹. + # to_s(): returns tcl/tk representation def to_s() return @exp end end -# class TclTkCommand: tcl/tk ¤Î¥³¥Þ¥ó¥É -# (tcltk ¥é¥¤¥Ö¥é¥ê¤ò»È¤¦¿Í¤¬ TclTkCommand.new() ¤¹¤ë¤³¤È¤Ï¤Ê¤¤¤Ï¤º. -# TclTkInterpreter:initialize() ¤«¤é new() ¤µ¤ì¤ë.) +# class TclTkCommand: tcl/tk commands +# you should not call TclTkCommand.new() +# commands are created by TclTkInterpreter:initialize() class TclTkCommand < TclTkObject - # e(*args): ¥³¥Þ¥ó¥É¤ò¼Â¹Ô¤·, ¤½¤Î·ë²Ì(String)¤òÊÖ¤¹. - # (e ¤Ï exec ¤Þ¤¿¤Ï eval ¤Î e.) - # *args: ¥³¥Þ¥ó¥É¤Î°ú¿ô + # e(*args): execute command. returns String (e is for exec or eval) + # *args: command arguments def e(*args) return @ip._eval_args(to_s(), *args) end end -# class TclTkLibCommand: tcl/tk ¤Î¥³¥Þ¥ó¥É -# (¥é¥¤¥Ö¥é¥ê¤Ë¤è¤ê¼Â¸½¤µ¤ì¤ë¥³¥Þ¥ó¥É¤Ç, tcl/tk ¥¤¥ó¥¿¥×¥ê¥¿¤ËºÇ½é¤«¤é -# ¸ºß¤·¤Ê¤¤¤â¤Î¤Ï, ¥¤¥ó¥¿¥×¥ê¥¿¤Î commands() ¤Ç¤ÏÀ¸À®¤Ç¤¤Ê¤¤. -# ¤½¤Î¤è¤¦¤Ê¤â¤Î¤ËÂФ·, ¥³¥Þ¥ó¥É¤Î̾Á°¤«¤é TclTkCommand ¥ª¥Ö¥¸¥§¥¯¥È¤ò -# À¸À®¤¹¤ë. +# class TclTkLibCommand: tcl/tk commands in the library class TclTkLibCommand < TclTkCommand - # initialize(ip, name): ½é´ü²½ - # ip: ¥¤¥ó¥¿¥×¥ê¥¿(TclTkInterpreter) - # name: ¥³¥Þ¥ó¥É̾ (String) + # initialize(ip, name): + # ip: interpreter(TclTkInterpreter) + # name: command name (String) def initialize(ip, name) super(ip._tcltkip, name) end end -# class TclTkVariable: tcl/tk ¤ÎÊÑ¿ô +# class TclTkVariable: tcl/tk variable class TclTkVariable < TclTkObject - # initialize(interp, dat): ½é´ü²½. - # interp: ¥¤¥ó¥¿¥×¥ê¥¿(TclTkInterpreter) - # dat: ÀßÄꤹ¤ëÃÍ(String) - # nil ¤Ê¤é, ÀßÄꤷ¤Ê¤¤. + # initialize(interp, dat): + # interp: interpreter(TclTkInterpreter) + # dat: the value to set(String) + # if nil, not initialize variable def initialize(interp, dat) - # tcl/tk ¤Ç¤Îɽ¸½·Á(ÊÑ¿ô̾)¤ò¼«Æ°À¸À®¤¹¤ë. + # auto-generate tcl/tk representation (variable name) exp = TclTk._newname("v_") - # TclTkObject ¤ò½é´ü²½¤¹¤ë. + # initialize TclTkObject super(interp._tcltkip(), exp) - # set ¥³¥Þ¥ó¥É¤ò»È¤¦¤Î¤Ç¤È¤Ã¤Æ¤ª¤¯. + # safe this for `set' command @set = interp.commands()["set"] - # ÃͤòÀßÄꤹ¤ë. + # set value set(dat) if dat end - # tcl/tk ¤Î set ¤ò»È¤¨¤Ð, ÃͤÎÀßÄê/»²¾È¤Ï¤Ç¤¤ë¤¬, - # ¤½¤ì¤À¤±¤Ç¤Ï¤Ê¤ó¤Ê¤Î¤Ç, °ì±þ, ¥á¥½¥Ã¥É¤ò¤«¤Ö¤»¤¿¤â¤Î¤âÍѰդ·¤Æ¤ª¤¯. + # although you can set/refer variable by using set in tcl/tk, + # we provide the method for accessing variables - # set(data): tcl/tk ¤ÎÊÑ¿ô¤Ë set ¤òÍѤ¤¤ÆÃͤòÀßÄꤹ¤ë. - # data: ÀßÄꤹ¤ëÃÍ + # set(data): set tcl/tk variable using `set' + # data: new value def set(data) @set.e(to_s(), data.to_s()) end - # get(): tcl/tk ¤ÎÊÑ¿ô¤ÎÃÍ(String)¤ò set ¤òÍѤ¤¤ÆÆÉ¤ß¤À¤·ÊÖ¤¹. + # get(): read tcl/tk variable(String) using `set' def get() return @set.e(to_s()) end end -# class TclTkWidget: tcl/tk ¤Î¥¦¥£¥¸¥§¥Ã¥È +# class TclTkWidget: tcl/tk widget class TclTkWidget < TclTkCommand - # initialize(*args): ½é´ü²½. - # *args: ¥Ñ¥é¥á¡¼¥¿ + # initialize(*args): + # *args: parameters def initialize(*args) if args[0].kind_of?(TclTkIp) - # ºÇ½é¤Î°ú¿ô¤¬ TclTkIp ¤Î¾ì¹ç: + # in case the 1st argument is TclTkIp: - # ´û¤Ë tcl/tk ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¥¦¥£¥¸¥§¥Ã¥È¤Ë TclTkWidget ¤Î¹½Â¤¤ò - # ¤«¤Ö¤»¤ë. (TclTkInterpreter:initialize() ¤«¤é»È¤ï¤ì¤ë.) + # Wrap tcl/tk widget by TclTkWidget + # (used in TclTkInterpreter#initialize()) - # ¥Ñ¥é¥á¡¼¥¿¿ô¤¬ 2 ¤Ç¤Ê¤±¤ì¤Ð¥¨¥é¡¼. + # need two arguments fail("illegal # of parameter") if args.size != 2 - # ip: ¥¤¥ó¥¿¥×¥ê¥¿(TclTkIp) - # exp: tcl/tk ¤Ç¤Îɽ¸½·Á + # ip: interpreter(TclTkIp) + # exp: tcl/tk representation ip, exp = args - # TclTkObject ¤ò½é´ü²½¤¹¤ë. + # initialize TclTkObject super(ip, exp) elsif args[0].kind_of?(TclTkInterpreter) - # ºÇ½é¤Î°ú¿ô¤¬ TclTkInterpreter ¤Î¾ì¹ç: + # in case 1st parameter is TclTkInterpreter: - # ¿Æ¥¦¥£¥¸¥§¥Ã¥È¤«¤é¿·¤¿¤Ê¥¦¥£¥¸¥§¥È¤òÀ¸À®¤¹¤ë. + # generate new widget from parent widget - # interp: ¥¤¥ó¥¿¥×¥ê¥¿(TclTkInterpreter) - # parent: ¿Æ¥¦¥£¥¸¥§¥Ã¥È - # command: ¥¦¥£¥¸¥§¥Ã¥È¤òÀ¸À®¤¹¤ë¥³¥Þ¥ó¥É(label Åù) - # *args: command ¤ËÅϤ¹°ú¿ô + # interp: interpreter(TclTkInterpreter) + # parent: parent widget + # command: widget generating tk command(label Åù) + # *args: argument to the command interp, parent, command, *args = args - # ¥¦¥£¥¸¥§¥Ã¥È¤Î̾Á°¤òºî¤ë. + # generate widget name exp = parent.to_s() exp += "." if exp !~ /[.]$/ exp += TclTk._newname("w_") - # TclTkObject ¤ò½é´ü²½¤¹¤ë. + # initialize TclTkObject super(interp._tcltkip(), exp) - # ¥¦¥£¥¸¥§¥Ã¥È¤òÀ¸À®¤¹¤ë. + # generate widget res = @ip._eval_args(command, exp, *args) # fail("can't create Widget") if res != exp - # tk_optionMenu ¤Ç¤Ï, ¥Ü¥¿¥ó̾¤ò exp ¤Ç»ØÄꤹ¤ë¤È - # res ¤Ë¥á¥Ë¥å¡¼Ì¾¤òÊÖ¤¹¤Î¤Ç res != exp ¤È¤Ê¤ë. + # for tk_optionMenu, it is legal res != exp else fail("first parameter is not TclTkInterpreter") end end end -# class TclTkCallback: tcl/tk ¤Î¥³¡¼¥ë¥Ð¥Ã¥¯ +# class TclTkCallback: tcl/tk callbacks class TclTkCallback < TclTkObject - # initialize(interp, pr, arg): ½é´ü²½. - # interp: ¥¤¥ó¥¿¥×¥ê¥¿(TclTkInterpreter) - # pr: ¥³¡¼¥ë¥Ð¥Ã¥¯¼ê³¤(Proc) - # arg: pr ¤Î¥¤¥Æ¥ì¡¼¥¿ÊÑ¿ô¤ËÅϤ¹Ê¸»úÎó - # tcl/tk ¤Î bind ¥³¥Þ¥ó¥É¤Ç¤Ï¥Ñ¥é¥á¡¼¥¿¤ò¼õ¤±¼è¤ë¤¿¤á¤Ë % ÃÖ´¹¤ò - # ÍѤ¤¤ë¤¬, pr ¤ÎÆâÉô¤Ç % ¤ò½ñ¤¤¤Æ¤â¤¦¤Þ¤¯¤¤¤«¤Ê¤¤. - # arg ¤Ëʸ»úÎó¤ò½ñ¤¤¤Æ¤ª¤¯¤È, ¤½¤ÎÃÖ´¹·ë²Ì¤ò, pr ¤Ç - # ¥¤¥Æ¥ì¡¼¥¿ÊÑ¿ô¤òÄ̤·¤Æ¼õ¤±¼è¤ë¤³¤È¤¬¤Ç¤¤ë. - # scrollbar ¥³¥Þ¥ó¥É¤Î -command ¥ª¥×¥·¥ç¥ó¤Î¤è¤¦¤Ë - # ²¿¤â»ØÄꤷ¤Ê¤¯¤Æ¤â¥Ñ¥é¥á¡¼¥¿¤¬ÉÕ¤¯¥³¥Þ¥ó¥É¤ËÂФ·¤Æ¤Ï, - # arg ¤ò»ØÄꤷ¤Æ¤Ï¤Ê¤é¤Ê¤¤. + # initialize(interp, pr, arg): + # interp: interpreter(TclTkInterpreter) + # pr: callback procedure(Proc) + # arg: string to pass as block parameters of pr + # bind command of tcl/tk uses % replacement for parameters + # pr can receive replaced data using block parameter + # its format is specified by arg string + # You should not specify arg for the command like + # scrollbar with -command option, which receives parameters + # without specifying any replacement def initialize(interp, pr, arg = nil) - # tcl/tk ¤Ç¤Îɽ¸½·Á(ÊÑ¿ô̾)¤ò¼«Æ°À¸À®¤¹¤ë. + # auto-generate tcl/tk representation (variable name) exp = TclTk._newname("c_") - # TclTkObject ¤ò½é´ü²½¤¹¤ë. + # initialize TclTkObject super(interp._tcltkip(), exp) - # ¥Ñ¥é¥á¡¼¥¿¤ò¤È¤Ã¤Æ¤ª¤¯. + # save parameters @pr = pr @arg = arg - # ¥â¥¸¥å¡¼¥ë¤ËÅÐÏ¿¤·¤Æ¤ª¤¯. + # register in the module TclTk._addcallback(self) end - # to_eval(): @ip._eval_args ¤Çɾ²Á¤¹¤ë¤È¤¤Îɽ¸½·Á(String)¤òÊÖ¤¹. + # to_eval(): retuens string representation for @ip._eval_args def to_eval() if @arg - # %s ¤Ï ruby_fmt ¤è¤êÁ°¤Ë bind ¤Ë¤è¤êÃÖ´¹¤µ¤ì¤Æ¤·¤Þ¤¦¤Î¤Ç - # %%s ¤È¤·¤Æ¤¢¤ë. ¤·¤¿¤¬¤Ã¤Æ, ¤³¤ì¤Ï bind ÀìÍÑ. + # bind replaces %s before calling ruby_fmt, so %%s is used s = %Q/{ruby_fmt {TclTk._callcallback("#{to_s()}", "%%s")} #{@arg}}/ else s = %Q/{ruby_fmt {TclTk._callcallback("#{to_s()}", "%s")}}/ @@ -358,28 +337,28 @@ class TclTkCallback < TclTkObject return s end - # _call(arg): ¥³¡¼¥ë¥Ð¥Ã¥¯¤ò¸Æ¤Ó½Ð¤¹. - # arg: ¥³¡¼¥ë¥Ð¥Ã¥¯¤ËÅϤµ¤ì¤ë¥Ñ¥é¥á¡¼¥¿ + # _call(arg): invoke callback + # arg: callback parameter def _call(arg) @pr.call(arg) end end -# class TclTkImage: tcl/tk ¤Î¥¤¥á¡¼¥¸ +# class TclTkImage: tcl/tk images class TclTkImage < TclTkCommand - # initialize(interp, t, *args): ½é´ü²½. - # ¥¤¥á¡¼¥¸¤ÎÀ¸À®¤Ï TclTkImage.new() ¤Ç¹Ô¤¦¤¬, - # Ç˲õ¤Ï image delete ¤Ç¹Ô¤¦. (¤¤¤Þ¤¤¤Á¤À¤±¤É»ÅÊý¤¬Ìµ¤¤.) - # interp: ¥¤¥ó¥¿¥×¥ê¥¿(TclTkInterpreter) - # t: ¥¤¥á¡¼¥¸¤Î¥¿¥¤¥× (photo, bitmap, etc.) - # *args: ¥³¥Þ¥ó¥É¤Î°ú¿ô + # initialize(interp, t, *args): + # generating image is done by TclTkImage.new() + # destrying is done by image delete (inconsistent, sigh) + # interp: interpreter(TclTkInterpreter) + # t: image type (photo, bitmap, etc.) + # *args: command argument def initialize(interp, t, *args) - # tcl/tk ¤Ç¤Îɽ¸½·Á(ÊÑ¿ô̾)¤ò¼«Æ°À¸À®¤¹¤ë. + # auto-generate tcl/tk representation exp = TclTk._newname("i_") - # TclTkObject ¤ò½é´ü²½¤¹¤ë. + # initialize TclTkObject super(interp._tcltkip(), exp) - # ¥¤¥á¡¼¥¸¤òÀ¸À®¤¹¤ë. + # generate image res = @ip._eval_args("image create", t, exp, *args) fail("can't create Image") if res != exp end diff --git a/ext/tcltklib/tcltklib.c b/ext/tcltklib/tcltklib.c index e7fe77d2b7..625fe61ccc 100644 --- a/ext/tcltklib/tcltklib.c +++ b/ext/tcltklib/tcltklib.c @@ -5,22 +5,31 @@ */ #include "ruby.h" -#include "sig.h" +#include "rubysig.h" #include <stdio.h> #include <string.h> #include <tcl.h> #include <tk.h> -/* for debug */ +#ifdef __MACOS__ +# include <tkMac.h> +# include <Quickdraw.h> +#endif -#define DUMP1(ARG1) if (debug) { fprintf(stderr, "tcltklib: %s\n", ARG1);} -#define DUMP2(ARG1, ARG2) if (debug) { fprintf(stderr, "tcltklib: ");\ +/* for rb_debug */ + +#define DUMP1(ARG1) if (rb_debug) { fprintf(stderr, "tcltklib: %s\n", ARG1);} +#define DUMP2(ARG1, ARG2) if (rb_debug) { fprintf(stderr, "tcltklib: ");\ fprintf(stderr, ARG1, ARG2); fprintf(stderr, "\n"); } /* #define DUMP1(ARG1) #define DUMP2(ARG1, ARG2) */ +/* for callback break & continue */ +VALUE eTkCallbackBreak; +VALUE eTkCallbackContinue; + /* from tkAppInit.c */ /* @@ -33,26 +42,52 @@ int *tclDummyMathPtr = (int *) matherr; /*---- module TclTkLib ----*/ -static VALUE thread_safe = Qnil; +/* Tk_ThreadTimer */ +typedef struct { + Tcl_TimerToken token; + int flag; +} Tk_TimerData; + +/* timer callback */ +void _timer_for_tcl (ClientData clientData) +{ + Tk_TimerData *timer = (Tk_TimerData*)clientData; + + timer->flag = 0; + CHECK_INTS; +#ifdef USE_THREAD + if (!rb_thread_critical) rb_thread_schedule(); +#endif + + timer->token = Tk_CreateTimerHandler(200, _timer_for_tcl, + (ClientData)timer); + timer->flag = 1; +} /* execute Tk_MainLoop */ static VALUE lib_mainloop(VALUE self) { - int old_trapflg; - int flags = RTEST(thread_safe)?TCL_DONT_WAIT:0; + Tk_TimerData *timer; + + timer = (Tk_TimerData *) ckalloc(sizeof(Tk_TimerData)); + timer->flag = 0; + timer->token = Tk_CreateTimerHandler(200, _timer_for_tcl, + (ClientData)timer); + timer->flag = 1; DUMP1("start Tk_Mainloop"); while (Tk_GetNumMainWindows() > 0) { - old_trapflg = trap_immediate; - trap_immediate = 1; - Tcl_DoOneEvent(flags); - trap_immediate = old_trapflg; - CHECK_INTS; - flags = (thread_safe == 0 || thread_safe == Qnil)?0:TCL_DONT_WAIT; + Tcl_DoOneEvent(0); } DUMP1("stop Tk_Mainloop"); +#ifdef USE_THREAD + if (timer->flag) { + Tk_DeleteTimerHandler(timer->token); + } +#endif + return Qnil; } @@ -71,27 +106,49 @@ ip_eval_rescue(VALUE *failed, VALUE einfo) } static int +#if TCL_MAJOR_VERSION >= 8 +ip_ruby(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *CONST argv[]) +#else ip_ruby(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) +#endif { VALUE res; int old_trapflg; VALUE failed = 0; + char *arg; + int dummy; /* ruby command has 1 arg. */ if (argc != 2) { - ArgError("wrong # of arguments (%d for 1)", argc); + rb_raise(rb_eArgError, "wrong # of arguments (%d for 1)", argc); } + /* get C string from Tcl object */ +#if TCL_MAJOR_VERSION >= 8 + arg = Tcl_GetStringFromObj(argv[1], &dummy); +#else + arg = argv[1]; +#endif + /* evaluate the argument string by ruby */ - DUMP2("rb_eval_string(%s)", argv[1]); - old_trapflg = trap_immediate; - trap_immediate = 0; - res = rb_rescue(rb_eval_string, argv[1], ip_eval_rescue, &failed); - trap_immediate = old_trapflg; + DUMP2("rb_eval_string(%s)", arg); + old_trapflg = rb_trap_immediate; + rb_trap_immediate = 0; + res = rb_rescue(rb_eval_string, (VALUE)arg, ip_eval_rescue, (VALUE)&failed); + rb_trap_immediate = old_trapflg; + Tcl_ResetResult(interp); if (failed) { - Tcl_AppendResult(interp, RSTRING(failed)->ptr, (char*)NULL); - return TCL_ERROR; + VALUE eclass = CLASS_OF(failed); + Tcl_AppendResult(interp, STR2CSTR(failed), (char*)NULL); + if (eclass == eTkCallbackBreak) { + return TCL_BREAK; + } else if (eclass == eTkCallbackContinue) { + return TCL_CONTINUE; + } else { + return TCL_ERROR; + } } /* result must be string or nil */ @@ -99,12 +156,11 @@ ip_ruby(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) DUMP1("(rb_eval_string result) nil"); return TCL_OK; } - Check_Type(res, T_STRING); /* copy result to the tcl interpreter */ - DUMP2("(rb_eval_string result) %s", RSTRING(res)->ptr); + DUMP2("(rb_eval_string result) %s", STR2CSTR(res)); DUMP1("Tcl_AppendResult"); - Tcl_AppendResult(interp, RSTRING(res)->ptr, (char *)NULL); + Tcl_AppendResult(interp, STR2CSTR(res), (char *)NULL); return TCL_OK; } @@ -115,6 +171,7 @@ ip_free(struct tcltkip *ptr) { DUMP1("Tcl_DeleteInterp"); Tcl_DeleteInterp(ptr->ip); + free(ptr); } /* create and initialize interpreter */ @@ -135,20 +192,26 @@ ip_new(VALUE self) /* from Tcl_AppInit() */ DUMP1("Tcl_Init"); if (Tcl_Init(ptr->ip) == TCL_ERROR) { - Fail("Tcl_Init"); + rb_raise(rb_eRuntimeError, "Tcl_Init"); } DUMP1("Tk_Init"); if (Tk_Init(ptr->ip) == TCL_ERROR) { - Fail("Tk_Init"); + rb_raise(rb_eRuntimeError, "Tk_Init"); } DUMP1("Tcl_StaticPackage(\"Tk\")"); Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL); /* add ruby command to the interpreter */ +#if TCL_MAJOR_VERSION >= 8 + DUMP1("Tcl_CreateObjCommand(\"ruby\")"); + Tcl_CreateObjCommand(ptr->ip, "ruby", ip_ruby, (ClientData *)NULL, + (Tcl_CmdDeleteProc *)NULL); +#else DUMP1("Tcl_CreateCommand(\"ruby\")"); Tcl_CreateCommand(ptr->ip, "ruby", ip_ruby, (ClientData *)NULL, (Tcl_CmdDeleteProc *)NULL); +#endif return obj; } @@ -157,6 +220,7 @@ ip_new(VALUE self) static VALUE ip_eval(VALUE self, VALUE str) { + char *s; char *buf; /* Tcl_Eval requires re-writable string region */ struct tcltkip *ptr; /* tcltkip data struct */ @@ -164,18 +228,162 @@ ip_eval(VALUE self, VALUE str) Data_Get_Struct(self, struct tcltkip, ptr); /* call Tcl_Eval() */ - Check_Type(str, T_STRING); - buf = ALLOCA_N(char,RSTRING(str)->len+1); - strcpy(buf, RSTRING(str)->ptr); + s = STR2CSTR(str); + buf = ALLOCA_N(char, strlen(s)+1); + strcpy(buf, s); DUMP2("Tcl_Eval(%s)", buf); ptr->return_value = Tcl_Eval(ptr->ip, buf); if (ptr->return_value == TCL_ERROR) { - Fail(ptr->ip->result); + rb_raise(rb_eRuntimeError, ptr->ip->result); } DUMP2("(TCL_Eval result) %d", ptr->return_value); /* pass back the result (as string) */ - return(str_new2(ptr->ip->result)); + return(rb_str_new2(ptr->ip->result)); +} + + +static VALUE +ip_toUTF8(VALUE self, VALUE str, VALUE encodename) +{ +#ifndef TCL_UTF_MAX + return str; +#else + Tcl_Interp *interp; + Tcl_Encoding encoding; + Tcl_DString dstr; + struct tcltkip *ptr; + char *buff1,*buff2; + + Data_Get_Struct(self,struct tcltkip, ptr); + interp = ptr->ip; + + encoding = Tcl_GetEncoding(interp,STR2CSTR(encodename)); + buff1 = ALLOCA_N(char,strlen(STR2CSTR(str))+1); + strcpy(buff1,STR2CSTR(str)); + + Tcl_DStringInit(&dstr); + Tcl_DStringFree(&dstr); + Tcl_ExternalToUtfDString(encoding,buff1,strlen(buff1),&dstr); + buff2 = ALLOCA_N(char,Tcl_DStringLength(&dstr)+1); + strcpy(buff2,Tcl_DStringValue(&dstr)); + + Tcl_FreeEncoding(encoding); + Tcl_DStringFree(&dstr); + + return rb_str_new2(buff2); +#endif +} + +static VALUE +ip_fromUTF8(VALUE self, VALUE str, VALUE encodename) +{ +#ifndef TCL_UTF_MAX + return str; +#else + Tcl_Interp *interp; + Tcl_Encoding encoding; + Tcl_DString dstr; + struct tcltkip *ptr; + char *buff1,*buff2; + + Data_Get_Struct(self,struct tcltkip, ptr); + interp = ptr->ip; + + encoding = Tcl_GetEncoding(interp,STR2CSTR(encodename)); + buff1 = ALLOCA_N(char,strlen(STR2CSTR(str))+1); + strcpy(buff1,STR2CSTR(str)); + + Tcl_DStringInit(&dstr); + Tcl_DStringFree(&dstr); + Tcl_UtfToExternalDString(encoding,buff1,strlen(buff1),&dstr); + buff2 = ALLOCA_N(char,Tcl_DStringLength(&dstr)+1); + strcpy(buff2,Tcl_DStringValue(&dstr)); + + Tcl_FreeEncoding(encoding); + Tcl_DStringFree(&dstr); + + return rb_str_new2(buff2); +#endif +} + + +static VALUE +ip_invoke(int argc, VALUE *argv, VALUE obj) +{ + struct tcltkip *ptr; /* tcltkip data struct */ + int i; + int object = 0; + Tcl_CmdInfo info; + char *cmd; + char **av = (char **)NULL; +#if TCL_MAJOR_VERSION >= 8 + Tcl_Obj **ov = (Tcl_Obj **)NULL; + Tcl_Obj *resultPtr; +#endif + + /* get the data struct */ + Data_Get_Struct(obj, struct tcltkip, ptr); + + /* get the command name string */ + cmd = STR2CSTR(argv[0]); + + /* map from the command name to a C procedure */ + if (!Tcl_GetCommandInfo(ptr->ip, cmd, &info)) { + rb_raise(rb_eNameError, "invalid command name `%s'", cmd); + } +#if TCL_MAJOR_VERSION >= 8 + object = info.isNativeObjectProc; +#endif + + /* memory allocation for arguments of this command */ + if (object) { +#if TCL_MAJOR_VERSION >= 8 + /* object interface */ + ov = (Tcl_Obj **)ALLOCA_N(Tcl_Obj *, argc+1); + for (i = 0; i < argc; ++i) { + char *s = STR2CSTR(argv[i]); + ov[i] = Tcl_NewStringObj(s, strlen(s)); + } + ov[argc] = (Tcl_Obj *)NULL; +#endif + } else { + /* string interface */ + av = (char **)ALLOCA_N(char *, argc+1); + for (i = 0; i < argc; ++i) { + char *s = STR2CSTR(argv[i]); + + av[i] = ALLOCA_N(char, strlen(s)+1); + strcpy(av[i], s); + } + av[argc] = (char *)NULL; + } + + Tcl_ResetResult(ptr->ip); + + /* Invoke the C procedure */ + if (object) { +#if TCL_MAJOR_VERSION >= 8 + int dummy; + ptr->return_value = (*info.objProc)(info.objClientData, + ptr->ip, argc, ov); + + /* get the string value from the result object */ + resultPtr = Tcl_GetObjResult(ptr->ip); + Tcl_SetResult(ptr->ip, Tcl_GetStringFromObj(resultPtr, &dummy), + TCL_VOLATILE); +#endif + } else { + ptr->return_value = (*info.proc)(info.clientData, + ptr->ip, argc, av); + } + + if (ptr->return_value == TCL_ERROR) { + rb_raise(rb_eRuntimeError, ptr->ip->result); + } + + /* pass back the result (as string) */ + return(rb_str_new2(ptr->ip->result)); } /* get return code from Tcl_Eval() */ @@ -190,27 +398,44 @@ ip_retval(VALUE self) return (INT2FIX(ptr->return_value)); } +#ifdef __MACOS__ +static void +_macinit() +{ + tcl_macQdPtr = &qd; /* setup QuickDraw globals */ + Tcl_MacSetEventProc(TkMacConvertEvent); /* setup event handler */ +} +#endif + /*---- initialization ----*/ void Init_tcltklib() { extern VALUE rb_argv0; /* the argv[0] */ VALUE lib = rb_define_module("TclTkLib"); - VALUE ip = rb_define_class("TclTkIp", cObject); + VALUE ip = rb_define_class("TclTkIp", rb_cObject); + + eTkCallbackBreak = rb_define_class("TkCallbackBreak", rb_eStandardError); + eTkCallbackContinue = rb_define_class("TkCallbackContinue",rb_eStandardError); rb_define_module_function(lib, "mainloop", lib_mainloop, 0); rb_define_singleton_method(ip, "new", ip_new, 0); rb_define_method(ip, "_eval", ip_eval, 1); + rb_define_method(ip, "_toUTF8",ip_toUTF8,2); + rb_define_method(ip, "_fromUTF8",ip_fromUTF8,2); + rb_define_method(ip, "_invoke", ip_invoke, -1); rb_define_method(ip, "_return_value", ip_retval, 0); rb_define_method(ip, "mainloop", lib_mainloop, 0); +#ifdef __MACOS__ + _macinit(); +#endif + /*---- initialize tcl/tk libraries ----*/ /* from Tk_Main() */ DUMP1("Tcl_FindExecutable"); Tcl_FindExecutable(RSTRING(rb_argv0)->ptr); - - rb_define_variable("$tk_thread_safe", &thread_safe); } /* eof */ diff --git a/ext/tk/MANIFEST b/ext/tk/MANIFEST new file mode 100644 index 0000000000..9689186bf0 --- /dev/null +++ b/ext/tk/MANIFEST @@ -0,0 +1,25 @@ +MANIFEST +extconf.rb +depend +tkutil.c +lib/tk.rb +lib/tkafter.rb +lib/tkbgerror.rb +lib/tkcanvas.rb +lib/tkclass.rb +lib/tkdialog.rb +lib/tkentry.rb +lib/tkfont.rb +lib/tkmenubar.rb +lib/tkmngfocus.rb +lib/tkpalette.rb +lib/tkscrollbox.rb +lib/tktext.rb +lib/tkvirtevent.rb +sample/tkbiff.rb +sample/tkbrowse.rb +sample/tkdialog.rb +sample/tkfrom.rb +sample/tkhello.rb +sample/tkline.rb +sample/tktimer.rb diff --git a/ext/tk/depend b/ext/tk/depend new file mode 100644 index 0000000000..e325a82c0f --- /dev/null +++ b/ext/tk/depend @@ -0,0 +1 @@ +tkutil.o: tkutil.c $(hdrdir)/ruby.h $(hdrdir)/config.h $(hdrdir)/defines.h diff --git a/ext/tk/extconf.rb b/ext/tk/extconf.rb new file mode 100644 index 0000000000..f769b06e30 --- /dev/null +++ b/ext/tk/extconf.rb @@ -0,0 +1,2 @@ +require 'mkmf' +create_makefile("tkutil") diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb new file mode 100644 index 0000000000..e32723be96 --- /dev/null +++ b/ext/tk/lib/tk.rb @@ -0,0 +1,2499 @@ +# +# tk.rb - Tk interface modue using tcltklib +# $Date$ +# by Yukihiro Matsumoto <matz@netlab.co.jp> + +# use Shigehiro's tcltklib +require "tcltklib" +require "tkutil" + +module TkComm + None = Object.new + def None.to_s + 'None' + end + + Tk_CMDTBL = {} + Tk_WINDOWS = {} + + def error_at + frames = caller() + frames.delete_if do |c| + c =~ %r!/tk(|core|thcore|canvas|text|entry|scrollbox)\.rb:\d+! + end + frames + end + private :error_at + + def _genobj_for_tkwidget(path) + return TkRoot.new if path == '.' + + begin + tk_class = TkCore::INTERP._invoke('winfo', 'class', path) + rescue + return path + end + + ruby_class = TkClassBind::WidgetClassNameTBL[tk_class] + gen_class_name = ruby_class.name + 'GeneratedOnTk' + unless Object.const_defined? gen_class_name + eval "class #{gen_class_name}<#{ruby_class.name} + def initialize(path) + @path=path + Tk_WINDOWS[@path] = self + end + end" + end + eval "#{gen_class_name}.new('#{path}')" + end + + def tk_tcl2ruby(val) + if val =~ /^rb_out (c\d+)/ + return Tk_CMDTBL[$1] + end + if val.include? ?\s + return val.split.collect{|v| tk_tcl2ruby(v)} + end + case val + when /^@font/ + TkFont.get_obj(val) + when /^-?\d+$/ + val.to_i + when /^\./ + Tk_WINDOWS[val] ? Tk_WINDOWS[val] : _genobj_for_tkwidget(val) + when / / + val.split.collect{|elt| + tk_tcl2ruby(elt) + } + when /^-?\d+\.\d*$/ + val.to_f + else + val + end + end + + def tk_split_list(str) + return [] if str == "" + idx = str.index('{') + return tk_tcl2ruby(str) unless idx + + list = tk_tcl2ruby(str[0,idx]) + list = [] if list == "" + str = str[idx+1..-1] + i = -1 + brace = 1 + str.each_byte {|c| + i += 1 + brace += 1 if c == ?{ + brace -= 1 if c == ?} + break if brace == 0 + } + if str[0, i] == ' ' + list.push ' ' + else + list.push tk_split_list(str[0, i]) + end + list += tk_split_list(str[i+1..-1]) + list + end + + def tk_split_simplelist(str) + return [] if str == "" + idx = str.index('{') + return str.split unless idx + + list = str[0,idx].split + str = str[idx+1..-1] + i = -1 + brace = 1 + str.each_byte {|c| + i += 1 + brace += 1 if c == ?{ + brace -= 1 if c == ?} + break if brace == 0 + } + if i == 0 + list.push '' + elsif str[0, i] == ' ' + list.push ' ' + else + list.push str[0..i-1] + end + list += tk_split_simplelist(str[i+1..-1]) + list + end + private :tk_tcl2ruby, :tk_split_list, :tk_split_simplelist + + def hash_kv(keys) + conf = [] + if keys and keys != None + for k, v in keys + conf.push("-#{k}") + conf.push(v) + end + end + conf + end + private :hash_kv + + def array2tk_list(ary) + ary.collect{|e| + if e.kind_of? Array + "{#{array2tk_list(e)}}" + elsif e.kind_of? Hash + "{#{e.to_a.collect{|ee| array2tk_list(ee)}.join(' ')}}" + else + s = _get_eval_string(e) + (s.index(/\s/))? "{#{s}}": s + end + }.join(" ") + end + private :array2tk_list + + def bool(val) + case val + when "1", 1, 'yes', 'true' + TRUE + else + FALSE + end + end + def number(val) + case val + when /^-?\d+$/ + val.to_i + when /^-?\d+\.\d*$/ + val.to_f + else + val + end + end + def string(val) + if val == "{}" + '' + elsif val[0] == ?{ + val[1..-2] + else + val + end + end + def list(val) + tk_split_list(val).to_a + end + def window(val) + Tk_WINDOWS[val] + end + def procedure(val) + if val =~ /^rb_out (c\d+)/ + Tk_CMDTBL[$1] + else + nil + end + end + private :bool, :number, :string, :list, :window, :procedure + + def _get_eval_string(str) + return nil if str == None + if str.kind_of?(Hash) + str = hash_kv(str).join(" ") + elsif str.kind_of?(Array) + str = array2tk_list(str) + elsif str.kind_of?(Proc) + str = install_cmd(str) + elsif str == nil + str = "" + elsif str == false + str = "0" + elsif str == true + str = "1" + elsif (str.respond_to?(:to_eval)) + str = str.to_eval() + else + str = str.to_s() + end + return str + end + private :_get_eval_string + + Tk_IDs = [0, 0] # [0]-cmdid, [1]-winid + def _curr_cmd_id + id = format("c%.4d", Tk_IDs[0]) + end + def _next_cmd_id + id = _curr_cmd_id + Tk_IDs[0] += 1 + id + end + def install_cmd(cmd) + return '' if cmd == '' + id = _next_cmd_id + Tk_CMDTBL[id] = cmd + @cmdtbl = [] if not @cmdtbl + @cmdtbl.push id + return format("rb_out %s", id); + end + def uninstall_cmd(id) + Tk_CMDTBL[id] = nil + end + private :install_cmd, :uninstall_cmd + + def install_win(ppath) + id = format("w%.4d", Tk_IDs[1]) + Tk_IDs[1] += 1 + if !ppath or ppath == "." + @path = format(".%s", id); + else + @path = format("%s.%s", ppath, id) + end + Tk_WINDOWS[@path] = self + end + + def uninstall_win() + Tk_WINDOWS[@path] = nil + end + + class Event + def initialize(seq,b,f,h,k,s,t,w,x,y,aa,ee,kk,nn,ww,tt,xx,yy) + @serial = seq + @num = b + @focus = (f == 1) + @height = h + @keycode = k + @state = s + @time = t + @width = w + @x = x + @y = y + @char = aa + @send_event = (ee == 1) + @keysym = kk + @keysym_num = nn + @type = tt + @widget = ww + @x_root = xx + @y_root = yy + end + attr :serial + attr :num + attr :focus + attr :height + attr :keycode + attr :state + attr :time + attr :width + attr :x + attr :y + attr :char + attr :send_event + attr :keysym + attr :keysym_num + attr :type + attr :widget + attr :x_root + attr :y_root + end + + def install_bind(cmd, args=nil) + if args + id = install_cmd(proc{|arg| + TkUtil.eval_cmd cmd, *arg + }) + id + " " + args + else + id = install_cmd(proc{|arg| + TkUtil.eval_cmd cmd, Event.new(*arg) + }) + id + ' %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y' + end + end + + def tk_event_sequence(context) + if context.kind_of? TkVirtualEvent + context = context.path + end + if context.kind_of? Array + context = context.collect{|ev| + if context.kind_of? TkVirtualEvent + ev.path + else + ev + end + }.join("><") + end + if /,/ =~ context + context = context.split(/\s*,\s*/).join("><") + else + context + end + end + + def _bind_core(mode, path, context, cmd, args=nil) + id = install_bind(cmd, args) + begin + tk_call 'bind', path, "<#{tk_event_sequence(context)}>", mode + id + rescue + uninstall_cmd(id) + fail + end + end + + def _bind(path, context, cmd, args=nil) + _bind_core('', path, context, cmd, args) + end + + def _bind_append(path, context, cmd, args=nil) + _bind_core('+', path, context, cmd, args) + end + private :install_bind, :tk_event_sequence, :_bind_core, :_bind, :_bind_append + + def bind_all(context, cmd=Proc.new, args=nil) + _bind 'all', context, cmd, args + end + + def bind_append_all(context, cmd=Proc.new, args=nil) + _bind_append 'all', context, cmd, args + end + + def bind(tagOrClass, context, cmd=Proc.new, args=nil) + _bind tagOrClass, context, cmd, args + end + + def bind_append(tagOrClass, context, cmd=Proc.new, args=nil) + _bind_append tagOrClass, context, cmd, args + end + + def _bindinfo(tagOrClass, context=nil) + if context + (tk_call('bind', tagOrClass, + "<#{tk_event_sequence(context)}>")).collect{|cmdline| + if cmdline =~ /^rb_out (c\d+)\s+(.*)$/ + [Tk_CMDTBL[$1], $2] + else + cmdline + end + } + else + tk_split_list(tk_call 'bind', tagOrClass).collect{|seq| + seq[1..-2].gsub(/></,',') + } + end + end + + def bindinfo(tagOrClass, context=nil) + _bindinfo tagOrClass, context + end + + def pack(*args) + TkPack.configure *args + end + + def grid(*args) + TkGrid.configure *args + end + + def update(idle=nil) + if idle + tk_call 'update', 'idletasks' + else + tk_call 'update' + end + end + +end + +module TkCore + include TkComm + extend TkComm + + INTERP = TclTkIp.new + + INTERP._invoke("proc", "rb_out", "args", "if {[set st [catch {ruby [format \"TkCore.callback %%Q!%s!\" $args]} ret]] != 0} {return -code $st $ret} {return $ret}") + + def callback_break + raise TkCallbackBreak, "Tk callback returns 'break' status" + end + + def callback_continue + raise TkCallbackContinue, "Tk callback returns 'continue' status" + end + + def after(ms, cmd=Proc.new) + myid = _curr_cmd_id + cmdid = install_cmd(cmd) + tk_call("after",ms,cmdid) + return + if false #defined? Thread + Thread.start do + ms = Float(ms)/1000 + ms = 10 if ms == 0 + sleep ms/1000 + cmd.call + end + else + cmdid = install_cmd(cmd) + tk_call("after",ms,cmdid) + end + end + + def TkCore.callback(arg) + arg = Array(tk_split_list(arg)) + _get_eval_string(TkUtil.eval_cmd(Tk_CMDTBL[arg.shift], *arg)) + end + + def appname(name=None) + tk_call('tk', 'appname', name) + end + + def appsend(interp, async, *args) + if async + tk_call('send', '-async', '--', interp, *args) + else + tk_call('send', '--', interp, *args) + end + end + + def rb_appsend(interp, async, *args) + args = args.filter{|c| _get_eval_string(c).gsub(/[][$"]/, '\\\\\&')} + args.push(').to_s"') + appsend(interp, async, 'ruby "(', *args) + end + + def appsend_displayof(interp, win, async, *args) + win = '.' if win == nil + if async + tk_call('send', '-async', '-displayof', win, '--', interp, *args) + else + tk_call('send', '-displayor', win, '--', interp, *args) + end + end + + def rb_appsend_displayof(interp, win, async, *args) + args = args.filter{|c| _get_eval_string(c).gsub(/[][$"]/, '\\\\\&')} + args.push(').to_s"') + appsend_displayof(interp, win, async, 'ruby "(', *args) + end + + def info(*args) + tk_call('info', *args) + end + + def mainloop + TclTkLib.mainloop + end + + def event_generate(window, context, keys=nil) + window = window.path if window.kind_of? TkObject + if keys + tk_call('event', 'generate', window, + "<#{tk_event_sequence(context)}>", *hash_kv(keys)) + else + tk_call('event', 'generate', window, "<#{tk_event_sequence(context)}>") + end + end + + def messageBox(keys) + tk_call 'tk_messageBox', *hash_kv(keys) + end + + def getOpenFile(keys) + tk_call 'tk_getOpenFile', *hash_kv(keys) + end + + def getSaveFile(keys) + tk_call 'tk_getSaveFile', *hash_kv(keys) + end + + def chooseColor(keys) + tk_call 'tk_chooseColor', *hash_kv(keys) + end + + def tk_call(*args) + print args.join(" "), "\n" if $DEBUG + args.filter {|x|_get_eval_string(x)} + args.compact! + args.flatten! + begin + res = INTERP._invoke(*args) + rescue NameError + err = $! + begin + args.unshift "unknown" + res = INTERP._invoke(*args) + rescue + raise unless /^invalid command/ =~ $! + raise err + end + end + if INTERP._return_value() != 0 + fail RuntimeError, res, error_at + end + print "==> ", res, "\n" if $DEBUG + return res + end +end + +module Tk + include TkCore + extend Tk + + TCL_VERSION = INTERP._invoke("info", "tclversion") + TK_VERSION = INTERP._invoke("set", "tk_version") + JAPANIZED_TK = (INTERP._invoke("info", "commands", "kanji") != "") + + def root + TkRoot.new + end + + def bell + tk_call 'bell' + end + + def Tk.focus(display=nil) + if display == nil + r = tk_call('focus') + else + r = tk_call('focus', '-displayof', display) + end + tk_tcl2ruby(r) + end + + def Tk.focus_lastfor(win) + tk_tcl2ruby(tk_call('focus', '-lastfor', win)) + end + + def toUTF8(str,encoding) + INTERP._toUTF8(str,encoding) + end + + def fromUTF8(str,encoding) + INTERP._fromUTF8(str,encoding) + end + + module Scrollable + def xscrollcommand(cmd=Proc.new) + configure_cmd 'xscrollcommand', cmd + end + def yscrollcommand(cmd=Proc.new) + configure_cmd 'yscrollcommand', cmd + end + end + + module Wm + def aspect(*args) + w = window(tk_call('wm', 'grid', path, *args)) + w.split.collect{|s|s.to_i} if args.length == 0 + end + def client(name=None) + tk_call 'wm', 'client', path, name + end + def colormapwindows(*args) + list(tk_call('wm', 'colormapwindows', path, *args)) + end + def wm_command(value=None) + string(tk_call('wm', 'command', path, value)) + end + def deiconify + tk_call 'wm', 'deiconify', path + end + def focusmodel(*args) + tk_call 'wm', 'focusmodel', path, *args + end + def frame + tk_call 'wm', 'frame', path + end + def geometry(*args) + list(tk_call('wm', 'geometry', path, *args)) + end + def grid(*args) + w = tk_call('wm', 'grid', path, *args) + list(w) if args.size == 0 + end + def group(*args) + tk_call 'wm', 'group', path, *args + end + def iconbitmap(*args) + tk_call 'wm', 'iconbitmap', path, *args + end + def iconify + tk_call 'wm', 'iconify', path + end + def iconmask(*args) + tk_call 'wm', 'iconmask', path, *args + end + def iconname(*args) + tk_call 'wm', 'iconname', path, *args + end + def iconposition(*args) + w = tk_call('wm', 'iconposition', path, *args) + list(w) if args.size == 0 + end + def iconwindow(*args) + w = tk_call('wm', 'iconwindow', path, *args) + window(w) if args.size == 0 + end + def maxsize(*args) + w = tk_call('wm', 'maxsize', path, *args) + list(w) if not args.size == 0 + end + def minsize(*args) + w = tk_call('wm', 'minsize', path, *args) + list(w) if args.size == 0 + end + def overrideredirect(bool=None) + if bool == None + bool(tk_call('wm', 'overrideredirect', path)) + else + tk_call 'wm', 'overrideredirect', path, bool + end + end + def positionfrom(*args) + tk_call 'wm', 'positionfrom', path, *args + end + def protocol(name=nil, cmd=nil) + if cmd + tk_call('wm', 'protocol', path, name, cmd) + elsif name + result = tk_call('wm', 'protocol', path, name) + (result == "")? nil : tk_tcl2ruby(result) + else + tk_split_simplelist(tk_call('wm', 'protocol', path)) + end + end + def resizable(*args) + w = tk_call('wm', 'resizable', path, *args) + if args.length == 0 + list(w).collect{|e| bool(e)} + end + end + def sizefrom(*args) + list(tk_call('wm', 'sizefrom', path, *args)) + end + def state + tk_call 'wm', 'state', path + end + def title(*args) + tk_call 'wm', 'title', path, *args + end + def transient(*args) + tk_call 'wm', 'transient', path, *args + end + def withdraw + tk_call 'wm', 'withdraw', path + end + end +end + +class TkVariable + include Tk + extend TkCore + + TkVar_CB_TBL = {} + Tk_VARIABLE_ID = ["v00000"] + + INTERP._invoke("proc", "rb_var", "args", "ruby [format \"TkVariable.callback %%Q!%s!\" $args]") + + def TkVariable.callback(args) + name1,name2,op = tk_split_list(args) + if TkVar_CB_TBL[name1] + _get_eval_string(TkVar_CB_TBL[name1].trace_callback(name2,op)) + else + '' + end + end + + def initialize(val="") + @id = Tk_VARIABLE_ID[0] + Tk_VARIABLE_ID[0] = Tk_VARIABLE_ID[0].succ + if val == [] + INTERP._eval(format('global %s; set %s(0) 0; unset %s(0)', + @id, @id, @id)) + elsif val.kind_of?(Array) + a = [] + val.each_with_index{|e,i| a.push(i); a.push(array2tk_list(e))} + s = '"' + a.join(" ").gsub(/[][$"]/, '\\\\\&') + '"' #' + INTERP._eval(format('global %s; array set %s %s', @id, @id, s)) + elsif val.kind_of?(Hash) + s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\ + .gsub(/[][$"]/, '\\\\\&') + '"' #' + INTERP._eval(format('global %s; array set %s %s', @id, @id, s)) + else + s = '"' + _get_eval_string(val).gsub(/[][$"]/, '\\\\\&') + '"' #' + INTERP._eval(format('global %s; set %s %s', @id, @id, s)) + end + end + + def wait + INTERP._eval("tkwait variable #{@id}") + end + + def id + @id + end + + def value + begin + INTERP._eval(format('global %s; set %s', @id, @id)) + rescue + if INTERP._eval(format('global %s; array exists %s', @id, @id)) != "1" + raise + else + Hash[*tk_split_simplelist(INTERP\ + ._eval(format('global %s; array get %s', + @id, @id)))] + end + end + end + + def value=(val) + begin + s = '"' + _get_eval_string(val).gsub(/[][$"]/, '\\\\\&') + '"' #' + INTERP._eval(format('global %s; set %s %s', @id, @id, s)) + rescue + if INTERP._eval(format('global %s; array exists %s', @id, @id)) != "1" + raise + else + INTERP._eval(format('global %s; unset %s'), @id, @id) + if val == [] + INTERP._eval(format('global %s; set %s(0) 0; unset %s(0)', + @id, @id, @id)) + elsif val.kind_of?(Array) + a = [] + val.each_with_index{|e,i| a.push(i); a.push(array2tk_list(e))} + s = '"' + a.join(" ").gsub(/[][$"]/, '\\\\\&') + '"' #' + INTERP._eval(format('global %s; unset %s; array set %s %s', + @id, @id, @id, s)) + elsif val.kind_of?(Hash) + s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\ + .gsub(/[][$"]/, '\\\\\&') + '"' #' + INTERP._eval(format('global %s; unset %s; array set %s %s', + @id, @id, @id, s)) + else + raise + end + end + end + end + + def [](index) + INTERP._eval(format('global %s; set %s(%s)', + @id, @id, _get_eval_string(index))) + end + + def []=(index,val) + INTERP._eval(format('global %s; set %s(%s) %s', @id, @id, + _get_eval_string(index), _get_eval_string(val))) + end + + def to_i + Integer(number(value)) + end + + def to_f + Float(number(value)) + end + + def to_s + String(string(value)) + end + + def inspect + format "<TkVariable: %s>", @id + end + + def ==(other) + case other + when TkVariable + self.equal(self) + when String + self.to_s == other + when Integer + self.to_i == other + when Float + self.to_f == other + when Array + self.to_a == other + else + false + end + end + + def to_a + list(value) + end + + def to_eval + @id + end + + def unset(elem=nil) + if elem + INTERP._eval(format('global %s; unset %s(%s)', + @id, @id, tk_tcl2ruby(elem))) + else + INTERP._eval(format('global %s; unset %s', @id, @id)) + end + end + alias remove unset + + def trace_callback(elem, op) + if @trace_var.kind_of? Array + @trace_var.each{|m,e| e.call(self,elem,op) if m.index(op)} + end + if elem.kind_of? String + if @trace_elem[elem].kind_of? Array + @trace_elem[elem].each{|m,e| e.call(self,elem,op) if m.index(op)} + end + end + end + + def trace(opts, cmd) + @trace_var = [] if @trace_var == nil + opts = ['r','w','u'].find_all{|c| opts.index(c)}.join('') + @trace_var.unshift([opts,cmd]) + if @trace_opts == nil + TkVar_CB_TBL[@id] = self + @trace_opts = opts + Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') + else + newopts = @trace_opts.dup + opts.each_byte{|c| newopts += c.chr unless @newopts.index(c)} + if newopts != @trace_opts + Tk.tk_call('trace', 'vdelete', @id, @trace_opts, 'rb_var') + @trace_opts.replace(newopts) + Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') + end + end + end + + def trace_element(elem, opts, cmd) + @trace_elem = {} if @trace_elem == nil + @trace_elem[elem] = [] if @trace_elem[elem] == nil + opts = ['r','w','u'].find_all{|c| opts.index(c)}.join('') + @trace_elem[elem].unshift([opts,cmd]) + if @trace_opts == nil + TkVar_CB_TBL[@id] = self + @trace_opts = opts + Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') + else + newopts = @trace_opts.dup + opts.each_byte{|c| newopts += c.chr unless @newopts.index(c)} + if newopts != @trace_opts + Tk.tk_call('trace', 'vdelete', @id, @trace_opts, 'rb_var') + @trace_opts.replace(newopts) + Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') + end + end + end + + def trace_vinfo + return [] unless @trace_var + @trace_var.dup + end + def trace_vinfo_for_element(elem) + return [] unless @trace_elem + return [] unless @trace_elem[elem] + @trace_elem[elem].dup + end + + def trace_vdelete(opts,cmd) + return unless @trace_var.kind_of? Array + opts = ['r','w','u'].find_all{|c| opts.index(c)}.join('') + idx = -1 + newopts = '' + @trace_var.each_with_index{|i,e| + if idx < 0 && e[0] == opts && e[1] == cmd + idx = i + next + end + e[0].each_byte{|c| newopts += c.chr unless newopts.index(c)} + } + if idx >= 0 + @trace_var.delete_at(idx) + else + return + end + + @trace_elem.each{|elem| + @trace_elem[elem].each{|e| + e[0].each_byte{|c| newopts += c.chr unless newopts.index(c)} + } + } + + newopts = ['r','w','u'].find_all{|c| newopts.index(c)}.join('') + if newopts != @trace_opts + Tk.tk_call('trace', 'vdelete', @id, @trace_opts, 'rb_var') + @trace_opts.replace(newopts) + if @trace_opts != '' + Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') + end + end + end + + def trace_vdelete_for_element(elem,opts,cmd) + return unless @trace_elem.kind_of? Hash + return unless @trace_elem[elem].kind_of? Array + opts = ['r','w','u'].find_all{|c| opts.index(c)}.join('') + idx = -1 + @trace_elem[elem].each_with_index{|i,e| + if idx < 0 && e[0] == opts && e[1] == cmd + idx = i + next + end + } + if idx >= 0 + @trace_elem[elem].delete_at(idx) + else + return + end + + newopts = '' + @trace_var.each{|e| + e[0].each_byte{|c| newopts += c.chr unless newopts.index(c)} + } + @trace_elem.each{|elem| + @trace_elem[elem].each{|e| + e[0].each_byte{|c| newopts += c.chr unless newopts.index(c)} + } + } + + newopts = ['r','w','u'].find_all{|c| newopts.index(c)}.join('') + if newopts != @trace_opts + Tk.tk_call('trace', 'vdelete', @id, @trace_opts, 'rb_var') + @trace_opts.replace(newopts) + if @trace_opts != '' + Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') + end + end + end +end + +class TkVarAccess<TkVariable + def initialize(varname, val=nil) + @id = varname + if val + s = '"' + _get_eval_string(val).gsub(/[][$"]/, '\\\\\&') + '"' #' + INTERP._eval(format('global %s; set %s %s', @id, @id, s)) + end + end +end + +module TkSelection + include Tk + extend Tk + def clear(win=Tk.root) + tk_call 'selection', 'clear', win.path + end + def get(type=None) + tk_call 'selection', 'get', type + end + def TkSelection.handle(win, func, type=None, format=None) + id = install_cmd(func) + tk_call 'selection', 'handle', win.path, id, type, format + end + def handle(func, type=None, format=None) + TkSelection.handle self, func, type, format + end + def TkSelection.own(win, func=None) + id = install_cmd(func) + tk_call 'selection', 'own', win.path, id + end + def own(func=None) + TkSelection.own self, func + end + + module_function :clear, :get +end + +module TkKinput + include Tk + extend Tk + + def TkKinput.start(window, style=None) + tk_call 'kinput_start', window.path, style + end + def kinput_start(style=None) + TkKinput.start(self, style) + end + + def TkKinput.send_spot(window) + tk_call 'kinput_send_spot', window.path + end + def kinput_send_spot + TkKinput.send_spot(self) + end + + def TkKinput.input_start(window, keys=nil) + tk_call 'kanjiInput', 'start', window.path, *hash_kv(keys) + end + def kanji_input_start(keys=nil) + TkKinput.input_start(self, keys) + end + + def TkKinput.attribute_config(window, slot, value=None) + if slot.kind_of? Hash + tk_call 'kanjiInput', 'attribute', window.path, *hash_kv(slot) + else + tk_call 'kanjiInput', 'attribute', window.path, "-#{slot}", value + end + end + def kinput_attribute_config(slot, value=None) + TkKinput.attribute_config(self, slot, value) + end + + def TkKinput.attribute_info(window, slot=nil) + if slot + conf = tk_split_list(tk_call('kanjiInput', 'attribute', + window.path, "-#{slot}")) + conf[0] = conf[0][1..-1] + conf + else + tk_split_list(tk_call('kanjiInput', 'attribute', + window.path)).collect{|conf| + conf[0] = conf[0][1..-1] + conf + } + end + end + def kinput_attribute_info(slot=nil) + TkKinput.attribute_info(self, slot) + end + + def TkKinput.input_end(window) + tk_call 'kanjiInput', 'end', window.path + end + def kanji_input_end + TkKinput.input_end(self) + end +end + +module TkWinfo + include Tk + extend Tk + def TkWinfo.atom(name) + tk_call 'winfo', name + end + def winfo_atom(name) + TkWinfo.atom name + end + def TkWinfo.atomname(id) + tk_call 'winfo', id + end + def winfo_atomname(id) + TkWinfo.atomname id + end + def TkWinfo.cells(window) + number(tk_call('winfo', window.path)) + end + def winfo_cells + TkWinfo.cells self + end + def TkWinfo.children(window) + c = tk_call('winfo', 'children', window.path) + list(c) + end + def winfo_children + TkWinfo.children self + end + def TkWinfo.classname(window) + tk_call 'winfo', 'class', window.path + end + def winfo_classname + TkWinfo.classname self + end + def TkWinfo.containing(rootX, rootY) + path = tk_call('winfo', 'containing', rootX, rootY) + window(path) + end + def winfo_containing(x, y) + TkWinfo.containing x, y + end + def TkWinfo.depth(window) + number(tk_call('winfo', 'depth', window.path)) + end + def winfo_depth + TkWinfo.depth self + end + def TkWinfo.exist?(window) + bool(tk_call('winfo', 'exists', window.path)) + end + def winfo_exist? + TkWinfo.exist? self + end + def TkWinfo.fpixels(window, number) + number(tk_call('winfo', 'fpixels', window.path, number)) + end + def winfo_fpixels(number) + TkWinfo.fpixels self, number + end + def TkWinfo.geometry(window) + list(tk_call('winfo', 'geometry', window.path)) + end + def winfo_geometry + TkWinfo.geometry self + end + def TkWinfo.height(window) + number(tk_call('winfo', 'height', window.path)) + end + def winfo_height + TkWinfo.height self + end + def TkWinfo.id(window) + number(tk_call('winfo', 'id', window.path)) + end + def winfo_id + TkWinfo.id self + end + def TkWinfo.interps(window=nil) + if window + tk_split_simplelist(tk_call('winfo', '-displayof', window.path, + 'interps')) + else + tk_split_simplelist(tk_call('winfo', 'interps')) + end + end + def winfo_interps + TkWinfo.interps self + end + def TkWinfo.mapped?(window) + bool(tk_call('winfo', 'ismapped', window.path)) + end + def winfo_mapped? + TkWinfo.mapped? self + end + def TkWinfo.appname(window) + bool(tk_call('winfo', 'name', window.path)) + end + def winfo_appname + TkWinfo.appname self + end + def TkWinfo.parent(window) + window(tk_call('winfo', 'parent', window.path)) + end + def winfo_parent + TkWinfo.parent self + end + def TkWinfo.widget(id) + window(tk_call('winfo', 'pathname', id)) + end + def winfo_widget(id) + TkWinfo.widget id + end + def TkWinfo.pixels(window, number) + number(tk_call('winfo', 'pixels', window.path, number)) + end + def winfo_pixels(number) + TkWinfo.pixels self, number + end + def TkWinfo.reqheight(window) + number(tk_call('winfo', 'reqheight', window.path)) + end + def winfo_reqheight + TkWinfo.reqheight self + end + def TkWinfo.reqwidth(window) + number(tk_call('winfo', 'reqwidth', window.path)) + end + def winfo_reqwidth + TkWinfo.reqwidth self + end + def TkWinfo.rgb(window, color) + list(tk_call('winfo', 'rgb', window.path, color)) + end + def winfo_rgb(color) + TkWinfo.rgb self, color + end + def TkWinfo.rootx(window) + number(tk_call('winfo', 'rootx', window.path)) + end + def winfo_rootx + TkWinfo.rootx self + end + def TkWinfo.rooty(window) + number(tk_call('winfo', 'rooty', window.path)) + end + def winfo_rooty + TkWinfo.rooty self + end + def TkWinfo.screen(window) + tk_call 'winfo', 'screen', window.path + end + def winfo_screen + TkWinfo.screen self + end + def TkWinfo.screencells(window) + number(tk_call('winfo', 'screencells', window.path)) + end + def winfo_screencells + TkWinfo.screencells self + end + def TkWinfo.screendepth(window) + number(tk_call('winfo', 'screendepth', window.path)) + end + def winfo_screendepth + TkWinfo.screendepth self + end + def TkWinfo.screenheight (window) + number(tk_call('winfo', 'screenheight', window.path)) + end + def winfo_screenheight + TkWinfo.screenheight self + end + def TkWinfo.screenmmheight(window) + number(tk_call('winfo', 'screenmmheight', window.path)) + end + def winfo_screenmmheight + TkWinfo.screenmmheight self + end + def TkWinfo.screenmmwidth(window) + number(tk_call('winfo', 'screenmmwidth', window.path)) + end + def winfo_screenmmwidth + TkWinfo.screenmmwidth self + end + def TkWinfo.screenvisual(window) + tk_call 'winfo', 'screenvisual', window.path + end + def winfo_screenvisual + TkWinfo.screenvisual self + end + def TkWinfo.screenwidth(window) + number(tk_call('winfo', 'screenwidth', window.path)) + end + def winfo_screenwidth + TkWinfo.screenwidth self + end + def TkWinfo.toplevel(window) + window(tk_call('winfo', 'toplevel', window.path)) + end + def winfo_toplevel + TkWinfo.toplevel self + end + def TkWinfo.visual(window) + tk_call 'winfo', 'visual', window.path + end + def winfo_visual + TkWinfo.visual self + end + def TkWinfo.vrootheigh(window) + number(tk_call('winfo', 'vrootheight', window.path)) + end + def winfo_vrootheight + TkWinfo.vrootheight self + end + def TkWinfo.vrootwidth(window) + number(tk_call('winfo', 'vrootwidth', window.path)) + end + def winfo_vrootwidth + TkWinfo.vrootwidth self + end + def TkWinfo.vrootx(window) + number(tk_call('winfo', 'vrootx', window.path)) + end + def winfo_vrootx + TkWinfo.vrootx self + end + def TkWinfo.vrooty(window) + number(tk_call('winfo', 'vrooty', window.path)) + end + def winfo_vrooty + TkWinfo.vrooty self + end + def TkWinfo.width(window) + number(tk_call('winfo', 'width', window.path)) + end + def winfo_width + TkWinfo.width self + end + def TkWinfo.x(window) + number(tk_call('winfo', 'x', window.path)) + end + def winfo_x + TkWinfo.x self + end + def TkWinfo.y(window) + number(tk_call('winfo', 'y', window.path)) + end + def winfo_y + TkWinfo.y self + end + def TkWinfo.viewable(window) + bool(tk_call 'winfo', 'viewable', window.path) + end + def winfo_viewable + TkWinfo.viewable self + end + def TkWinfo.pointerx(window) + number(tk_call('winfo', 'pointerx', window.path)) + end + def winfo_pointerx + TkWinfo.pointerx self + end + def TkWinfo.pointery(window) + number(tk_call('winfo', 'pointery', window.path)) + end + def winfo_pointery + TkWinfo.pointery self + end + def TkWinfo.pointerxy(window) + list(tk_call('winfo', 'pointerxy', window.path)) + end + def winfo_pointerxy + TkWinfo.pointerxy self + end +end + +module TkPack + include Tk + extend Tk + def configure(win, *args) + if args[-1].kind_of?(Hash) + keys = args.pop + end + wins = [win.epath] + for i in args + wins.push i.epath + end + tk_call "pack", 'configure', *(wins+hash_kv(keys)) + end + + def forget(*args) + tk_call 'pack', 'forget' *args + end + + def propagate(master, bool=None) + bool(tk_call('pack', 'propagate', master.epath, bool)) + end + module_function :configure, :forget, :propagate +end + +module TkGrid + include Tk + extend Tk + + def bbox(*args) + list(tk_call('grid', 'bbox', *args)) + end + + def configure(widget, *args) + if args[-1].kind_of?(Hash) + keys = args.pop + end + wins = [widget.epath] + for i in args + wins.push i.epath + end + tk_call "grid", 'configure', *(wins+hash_kv(keys)) + end + + def columnconfigure(master, index, args) + tk_call "grid", 'columnconfigure', master, index, *hash_kv(args) + end + + def rowconfigure(master, index, args) + tk_call "grid", 'rowconfigure', master, index, *hash_kv(args) + end + + def add(widget, *args) + configure(widget, *args) + end + + def forget(*args) + tk_call 'grid', 'forget', *args + end + + def info(slave) + list(tk_call('grid', 'info', slave)) + end + + def location(master, x, y) + list(tk_call('grid', 'location', master, x, y)) + end + + def propagate(master, bool=None) + bool(tk_call('grid', 'propagate', master.epath, bool)) + end + + def remove(*args) + tk_call 'grid', 'remove', *args + end + + def size(master) + tk_call 'grid', 'size', master + end + + def slaves(args) + list(tk_call('grid', 'slaves', *hash_kv(args))) + end + + module_function :bbox, :forget, :propagate, :info + module_function :remove, :size, :slaves, :location + module_function :configure, :columnconfigure, :rowconfigure +end + +module TkOption + include Tk + extend Tk + def add pat, value, pri=None + tk_call 'option', 'add', pat, value, pri + end + def clear + tk_call 'option', 'clear' + end + def get win, name, klass + tk_call 'option', 'get', win ,name, klass + end + def readfile file, pri=None + tk_call 'option', 'readfile', file, pri + end + module_function :add, :clear, :get, :readfile +end + +module TkTreatFont + def font_configinfo + ret = TkFont.used_on(self.path) + if ret == nil + ret = TkFont.init_widget_font(self.path, self.path, 'configure') + end + ret + end + alias fontobj font_configinfo + + def font_configure(slot) + if (fnt = slot['font']) + slot['font'] = nil + if fnt.kind_of? TkFont + return fnt.call_font_configure(self.path, self.path,'configure',slot) + else + latinfont_configure(fnt) if fnt + end + end + if (ltn = slot['latinfont']) + slot['latinfont'] = nil + latinfont_configure(ltn) if ltn + end + if (ltn = slot['asciifont']) + slot['asciifont'] = nil + latinfont_configure(ltn) if ltn + end + if (knj = slot['kanjifont']) + slot['kanjifont'] = nil + kanjifont_configure(knj) if knj + end + + tk_call(self.path, 'configure', *hash_kv(slot)) if slot != {} + self + end + + def latinfont_configure(ltn, keys=nil) + fobj = fontobj + if ltn.kind_of? TkFont + conf = {} + ltn.latin_configinfo.each{|key,val| conf[key] = val} + if keys + fobj.latin_configure(conf.update(keys)) + else + fobj.latin_configure(conf) + end + else + fobj.latin_replace(ltn) + end + end + alias asciifont_configure latinfont_configure + + def kanjifont_configure(knj, keys=nil) + fobj = fontobj + if knj.kind_of? TkFont + conf = {} + knj.kanji_configinfo.each{|key,val| conf[key] = val} + if keys + fobj.kanji_configure(conf.update(keys)) + else + fobj.kanji_configure(cond) + end + else + fobj.kanji_replace(knj) + end + end + + def font_copy(window, tag=nil) + if tag + window.tagfontobj(tag).configinfo.each{|key,value| + fontobj.configure(key,value) + } + fontobj.replace(window.tagfontobj(tag).latin_font, + window.tagfontobj(tag).kanji_font) + else + window.fontobj.configinfo.each{|key,value| + fontobj.configure(key,value) + } + fontobj.replace(window.fontobj.latin_font, window.fontobj.kanji_font) + end + end + + def latinfont_copy(window, tag=nil) + if tag + fontobj.latin_replace(window.tagfontobj(tag).latin_font) + else + fontobj.latin_replace(window.fontobj.latin_font) + end + end + alias asciifont_copy latinfont_copy + + def kanjifont_copy(window, tag=nil) + if tag + fontobj.kanji_replace(window.tagfontobj(tag).kanji_font) + else + fontobj.kanji_replace(window.fontobj.kanji_font) + end + end +end + +class TkObject<TkKernel + include Tk + include TkTreatFont + + def path + return @path + end + + def epath + return @path + end + + def to_eval + @path + end + + def tk_send(cmd, *rest) + tk_call path, cmd, *rest + end + private :tk_send + + def method_missing(id, *args) + name = id.id2name + case args.length + when 1 + configure name, args[0] + when 0 + begin + cget name + rescue + fail NameError, "undefined local variable or method `#{name}' for #{self.to_s}", error_at + end + else + fail NameError, "undefined method `#{name}' for #{self.to_s}", error_at + end + end + + def [](id) + cget id + end + + def []=(id, val) + configure id, val + end + + def cget(slot) + tk_tcl2ruby tk_call path, 'cget', "-#{slot}" + end + + def configure(slot, value=None) + if slot.kind_of? Hash + if ( slot['font'] || slot['kanjifont'] \ + || slot['latinfont'] || slot['asciifont'] ) + font_configure(slot.dup) + else + tk_call path, 'configure', *hash_kv(slot) + end + + else + if ( slot == 'font' || slot == 'kanjifont' \ + || slot == 'latinfont' || slot == 'asciifont' ) + font_configure({slot=>value}) + else + tk_call path, 'configure', "-#{slot}", value + end + end + end + + def configure_cmd(slot, value) + configure slot, install_cmd(value) + end + + def configinfo(slot = nil) + if slot == 'font' || slot == 'kanjifont' + fontobj + + else + if slot + conf = tk_split_list(tk_send('configure', "-#{slot}") ) + conf[0] = conf[0][1..-1] + conf + + else + ret = tk_split_list(tk_send('configure') ).collect{|conf| + conf[0] = conf[0][1..-1] + conf + } + if ret.assoc('font') + ret.delete_if{|item| item[0] == 'font' || item[0] == 'kanjifont'} + ret.push(['font', fontobj]) + else + ret + end + end + end + end + + def bind(context, cmd=Proc.new, args=nil) + _bind path, context, cmd, args + end + + def bind_append(context, cmd=Proc.new, args=nil) + _bind_append path, context, cmd, args + end + + def bindinfo(context=nil) + _bindinfo path, context + end + + def event_generate(context, keys=nil) + if keys + tk_call('event', 'generate', path, + "<#{tk_event_sequence(context)}>", *hash_kv(keys)) + else + tk_call('event', 'generate', path, "<#{tk_event_sequence(context)}>") + end + end + + def tk_trace_variable(v) + unless v.kind_of?(TkVariable) + fail ArgumentError, format("requires TkVariable given %s", v.type) + end + v + end + private :tk_trace_variable + + def destroy + tk_call 'trace', 'vdelete', @tk_vn, 'w', @var_id if @var_id + end +end + +module TkClassBind + WidgetClassNameTBL = {} + + def TkClassBind.name2class(name) + WidgetClassNameTBL[name] + end + + def bind(context, cmd=Proc.new, args=nil) + Tk.bind to_eval, context, cmd, args + end + + def bind_append(context, cmd=Proc.new, args=nil) + Tk.bind_append to_eval, context, cmd, args + end + + def bindinfo(context=nil) + Tk.bindinfo to_eval, context + end +end + +class TkWindow<TkObject + extend TkClassBind + + def initialize(parent=nil, keys=nil) + install_win(if parent then parent.path end) + create_self + if keys + # tk_call @path, 'configure', *hash_kv(keys) + configure(keys) + end + end + + def create_self + end + private :create_self + + def pack(keys = nil) + tk_call 'pack', epath, *hash_kv(keys) + self + end + + def unpack(keys = nil) + tk_call 'pack', 'forget', epath + self + end + + def grid(keys = nil) + tk_call 'grid', epath, *hash_kv(keys) + self + end + + def ungrid(keys = nil) + tk_call 'grid', 'forget', epath + self + end + + def place(keys = nil) + tk_call 'place', epath, *hash_kv(keys) + self + end + + def unplace(keys = nil) + tk_call 'place', 'forget', epath, *hash_kv(keys) + self + end + alias place_forget unplace + + def place_config(keys) + tk_call "place", 'configure', epath, *hash_kv(keys) + end + + def place_info() + ilist = list(tk_call('place', 'info', epath)) + info = {} + while key = ilist.shift + info[key[1..-1]] = ilist.shift + end + return info + end + + def pack_slaves() + list(tk_call('pack', 'slaves', epath)) + end + + def pack_info() + ilist = list(tk_call('pack', 'info', epath)) + info = {} + while key = ilist.shift + info[key[1..-1]] = ilist.shift + end + return info + end + + def place_slaves() + list(tk_call('place', 'slaves', epath)) + end + + def focus(force=false) + if force + tk_call 'focus', '-force', path + else + tk_call 'focus', path + end + self + end + + def grab(*args) + if !args or args.length == 0 + tk_call 'grab', 'set', path + elsif args.length == 1 + case args[0] + when 'global' + tk_call 'grab', 'set', '-global', path + else + val = tk_call('grab', args[0], path) + end + case args[0] + when 'current' + return window(val) + when 'status' + return val + end + else + fail ArgumentError, 'wrong # of args' + end + end + + def lower(below=None) + tk_call 'lower', path, below + self + end + def raise(above=None) + tk_call 'raise', path, above + self + end + + def command(cmd=Proc.new) + configure_cmd 'command', cmd + end + + def colormodel model=None + tk_call 'tk', 'colormodel', path, model + self + end + + def destroy + tk_call 'destroy', path + if @cmdtbl + for id in @cmdtbl + uninstall_cmd id + end + end + uninstall_win + end + + def wait_visibility + tk_call 'tkwait', 'visibility', path + end + alias wait wait_visibility + + def wait_destroy + tk_call 'tkwait', 'window', path + end + + def bindtags(taglist=nil) + if taglist + fail unless taglist.kind_of? Array + tk_call('bindtags', path, taglist) + else + tk_split_list(tk_call('bindtags', path)).collect{|tag| + if tag == nil + '.' + elsif tag.kind_of?(String) && (cls = TkClassBind.name2class(tag)) + cls + else + tag + end + } + end + end +end + +class TkRoot<TkWindow + include Wm + ROOT = [] + def TkRoot.new + return ROOT[0] if ROOT[0] + new = super + ROOT[0] = new + Tk_WINDOWS["."] = new + end + + WidgetClassName = 'Tk'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + + def create_self + @path = '.' + end + def path + "." + end +end + +class TkToplevel<TkWindow + include Wm + + WidgetClassName = 'Toplevel'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + + def initialize(parent=nil, screen=nil, classname=nil, keys=nil) + if screen.kind_of? Hash + keys = screen.dup + else + @screen = screen + end + @classname = classname + if keys.kind_of? Hash + keys = keys.dup + if keys['classname'] + @classname = keys['classname'] + keys['classname'] = nil + end + if keys['colormap'] + @colormap = keys['colormap'] + keys['colormap'] = nil + end + if keys['container'] + @classname = keys['container'] + keys['classname'] = nil + end + if keys['screen'] + @screen = keys['screen'] + keys['screen'] = nil + end + if keys['use'] + @use = keys['use'] + keys['use'] = nil + end + if keys['visual'] + @screen = keys['visual'] + keys['visual'] = nil + end + end + super(parent, keys) + end + + def create_self + s = [] + s.push << "-class" << @classname if @classname + s.push << "-colormap" << @colormap if @colormap + s.push << "-container" << @container if @container + s.push << "-screen" << @screen if @screen + s.push << "-use" << @use if @use + s.push << "-visual" << @visual if @visual + tk_call 'toplevel', @path, *s + end + + def specific_class + @classname + end +end + +class TkFrame<TkWindow + WidgetClassName = 'Frame'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + + def initialize(parent=nil, keys=nil) + if keys.kind_of? Hash + keys = keys.dup + if keys['classname'] + @classname = keys['classname'] + keys['classname'] = nil + end + if keys['colormap'] + @colormap = keys['colormap'] + keys['colormap'] = nil + end + if keys['container'] + @classname = keys['container'] + keys['classname'] = nil + end + if keys['visual'] + @screen = keys['visual'] + keys['visual'] = nil + end + end + super(parent, keys) + end + + def create_self + s = [] + s.push << "-class" << @classname if @classname + s.push << "-colormap" << @colormap if @colormap + s.push << "-container" << @container if @container + s.push << "-visual" << @visual if @visual + tk_call 'frame', @path, *s + end +end + +class TkLabel<TkWindow + WidgetClassName = 'Label'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + def create_self + tk_call 'label', @path + end + def textvariable(v) + configure 'textvariable', tk_trace_variable(v) + end +end + +class TkButton<TkLabel + WidgetClassName = 'Button'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self +# def TkButton.to_eval + def self.to_eval + WidgetClassName + end + def create_self + tk_call 'button', @path + end + def invoke + tk_send 'invoke' + end + def flash + tk_send 'flash' + end +end + +class TkRadioButton<TkButton + WidgetClassName = 'Radiobutton'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def TkRadioButton.to_eval + WidgetClassName + end + def create_self + tk_call 'radiobutton', @path + end + def deselect + tk_send 'deselect' + end + def select + tk_send 'select' + end + def variable(v) + configure 'variable', tk_trace_variable(v) + end +end + +class TkCheckButton<TkRadioButton + TkClassBind::WidgetClassNameTBL['Checkbutton'] = self + def TkCheckButton.to_eval + 'Checkbutton' + end + def create_self + tk_call 'checkbutton', @path + end + def toggle + tk_send 'toggle' + end +end + +class TkMessage<TkLabel + TkClassBind::WidgetClassNameTBL['Message'] = self + def TkMessage.to_eval + 'Message' + end + def create_self + tk_call 'message', @path + end +end + +class TkScale<TkWindow + WidgetClassName = 'Scale'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + + def create_self + tk_call 'scale', path + end + + def get + number(tk_send('get')) + end + + def set(val) + tk_send "set", val + end + + def value + get + end + + def value= (val) + set val + end +end + +class TkScrollbar<TkWindow + WidgetClassName = 'Scrollbar'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + + def create_self + tk_call 'scrollbar', path + end + + def delta(deltax=None, deltay=None) + number(tk_send('delta', deltax, deltay)) + end + + def fraction(x=None, y=None) + number(tk_send('fraction', x, y)) + end + + def identify(x=None, y=None) + tk_send('fraction', x, y) + end + + def get + ary1 = tk_send('get', path).split + ary2 = [] + for i in ary1 + ary2.push number(i) + end + ary2 + end + + def set(first, last) + tk_send "set", first, last + end +end + +class TkTextWin<TkWindow + def create_self + raise TypeError, "TkTextWin is abstract class" + end + + def bbox(index) + tk_send 'bbox', index + end + def delete(first, last=None) + tk_send 'delete', first, last + end + def get(*index) + tk_send 'get', *index + end + def index(index) + tk_send 'index', index + end + def insert(index, chars, *args) + tk_send 'insert', index, chars, *args + end + def scan_mark(x, y) + tk_send 'scan', 'mark', x, y + end + def scan_dragto(x, y) + tk_send 'scan', 'dragto', x, y + end + def see(index) + tk_send 'see', index + end +end + +class TkListbox<TkTextWin + TkClassBind::WidgetClassNameTBL['Listbox'] = self + def TkListbox.to_eval + 'Listbox' + end + def create_self + tk_call 'listbox', path + end + + def activate(y) + tk_send 'activate', y + end + def curselection + list(tk_send('curselection')) + end + def nearest(y) + tk_send('nearest', y).to_i + end + def size(y) + tk_send('size').to_i + end + def selection_anchor(index) + tk_send 'selection', 'anchor', index + end + def selection_clear(first, last=None) + tk_send 'selection', 'clear', first, last + end + def selection_includes + bool(tk_send('selection', 'includes')) + end + def selection_set(first, last=None) + tk_send 'selection', 'set', first, last + end + def xview(cmd, index, *more) + v = tk_send('xview', cmd, index, *more) + v.to_i if more.size == 0 + end + def yview(cmd, index, *more) + v = tk_send('yview', cmd, index, *more) + v.to_i if more.size == 0 + end +end + +module TkTreatMenuEntryFont + def tagfont_configinfo(index) + pathname = self.path + ';' + index + ret = TkFont.used_on(pathname) + if ret == nil + ret = TkFont.init_widget_font(pathname, + self.path, 'entryconfigure', index) + end + ret + end + alias tagfontobj tagfont_configinfo + + def tagfont_configure(index, slot) + pathname = self.path + ';' + index + if (fnt = slot['font']) + slot['font'] = nil + if fnt.kind_of? TkFont + return fnt.call_font_configure(pathname, + self.path,'entryconfigure',index,slot) + else + latintagfont_configure(index, fnt) if fnt + end + end + if (ltn = slot['latinfont']) + slot['latinfont'] = nil + latintagfont_configure(index, ltn) if ltn + end + if (ltn = slot['asciifont']) + slot['asciifont'] = nil + latintagfont_configure(index, ltn) if ltn + end + if (knj = slot['kanjifont']) + slot['kanjifont'] = nil + kanjitagfont_configure(index, knj) if knj + end + + tk_call(self.path, 'entryconfigure', index, *hash_kv(slot)) if slot != {} + self + end + + def latintagfont_configure(index, ltn, keys=nil) + fobj = tagfontobj(index) + if ltn.kind_of? TkFont + conf = {} + ltn.latin_configinfo.each{|key,val| conf[key] = val if val != []} + if conf == {} + fobj.latin_replace(ltn) + fobj.latin_configure(keys) if keys + elsif keys + fobj.latin_configure(conf.update(keys)) + else + fobj.latin_configure(conf) + end + else + fobj.latin_replace(ltn) + end + end + alias asciitagfont_configure latintagfont_configure + + def kanjitagfont_configure(index, knj, keys=nil) + fobj = tagfontobj(index) + if knj.kind_of? TkFont + conf = {} + knj.kanji_configinfo.each{|key,val| conf[key] = val if val != []} + if conf == {} + fobj.kanji_replace(knj) + fobj.kanji_configure(keys) if keys + elsif keys + fobj.kanji_configure(conf.update(keys)) + else + fobj.kanji_configure(conf) + end + else + fobj.kanji_replace(knj) + end + end + + def tagfont_copy(index, window, wintag=nil) + if wintag + window.tagfontobj(wintag).configinfo.each{|key,value| + tagfontobj(index).configure(key,value) + } + tagfontobj(index).replace(window.tagfontobj(wintag).latin_font, + window.tagfontobj(wintag).kanji_font) + else + window.tagfont(wintag).configinfo.each{|key,value| + tagfontobj(index).configure(key,value) + } + tagfontobj(index).replace(window.fontobj.latin_font, + window.fontobj.kanji_font) + end + end + + def latintagfont_copy(index, window, wintag=nil) + if wintag + tagfontobj(index).latin_replace(window.tagfontobj(wintag).latin_font) + else + tagfontobj(index).latin_replace(window.fontobj.latin_font) + end + end + alias asciitagfont_copy latintagfont_copy + + def kanjitagfont_copy(index, window, wintag=nil) + if wintag + tagfontobj(index).kanji_replace(window.tagfontobj(wintag).kanji_font) + else + tagfontobj(index).kanji_replace(window.fontobj.kanji_font) + end + end +end + +class TkMenu<TkWindow + include TkTreatMenuEntryFont + + WidgetClassName = 'Menu'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + def create_self + tk_call 'menu', path + end + def activate(index) + tk_send 'activate', index + end + def add(type, keys=nil) + tk_send 'add', type, *hash_kv(keys) + end + def index(index) + tk_send 'index', index + end + def invoke(index) + tk_send 'invoke', index + end + def insert(index, type, keys=nil) + tk_send 'add', index, type, *hash_kv(keys) + end + def delete(index, last=None) + tk_send 'delete', index, last + end + def post(x, y) + tk_send 'post', x, y + end + def postcascade(index) + tk_send 'postcascade', index + end + def postcommand(cmd=Proc.new) + configure_cmd 'postcommand', cmd + end + def menutype(index) + tk_send 'type', index + end + def unpost + tk_send 'unpost' + end + def yposition(index) + number(tk_send('yposition', index)) + end + def entryconfigure(index, keys=nil) + tk_send 'entryconfigure', index, *hash_kv(keys) + end +# def entryconfigure(index, keys=nil) +# tk_send 'entryconfigure', index, *hash_kv(keys) +# end + def entrycget(index, key) + tk_tcl2ruby tk_send 'entrycget', index, "-#{key}" + end + def entryconfigure(index, key, val=None) + if key.kind_of? Hash + if ( key['font'] || key['kanjifont'] \ + || key['latinfont'] || key['asciifont'] ) + tagfont_configure(index, key.dup) + else + tk_send 'entryconfigure', index, *hash_kv(key) + end + + else + if ( key == 'font' || key == 'kanjifont' \ + || key == 'latinfont' || key == 'asciifont' ) + tagfont_configure({key=>val}) + else + tk_call 'entryconfigure', index, "-#{key}", val + end + end + end + + def entryconfiginfo(index, key=nil) + if key + conf = tk_split_list(tk_send('entryconfigure',index,"-#{key}")) + conf[0] = conf[0][1..-1] + conf + else + tk_split_list(tk_send('entryconfigure', index)).collect{|conf| + conf[0] = conf[0][1..-1] + conf + } + end + end +end + +class TkMenubutton<TkLabel + TkClassBind::WidgetClassNameTBL['Menubutton'] = self + def TkMenubutton.to_eval + 'Menubutton' + end + def create_self + tk_call 'menubutton', path + end +end + +module TkComposite + def initialize(parent=nil, *args) + @frame = TkFrame.new(parent) + @path = @epath = @frame.path + initialize_composite(*args) + end + + def epath + @epath + end + + def initialize_composite(*args) end + private :initialize_composite + + def delegate(option, *wins) + unless @delegates + @delegates = {} + @delegates['DEFAULT'] = @frame + end + if @delegates[option].kind_of?(Array) + for i in wins + @delegates[option].push(i) + end + else + @delegates[option] = wins + end + end + + def configure(slot, value=None) + if slot.kind_of? Hash + slot.each{|slot,value| configure slot, value} + else + if @delegates and @delegates[slot] + for i in @delegates[slot] + if not i + i = @delegates['DEFALUT'] + redo + else + last = i.configure(slot, value) + end + end + last + else + super + end + end + end +end + +module TkClipboard + include Tk + extend Tk + + def clear + tk_call 'clipboard', 'clear' + end + def get + begin + tk_call 'selection', 'get', '-selection', 'CLIPBOARD' + rescue + '' + end + end + def set(data) + clear + append(data) + end + def append(data) + tk_call 'clipboard', 'append', data + end + + module_function :clear, :set, :get, :append +end + +autoload :TkCanvas, 'tkcanvas' +autoload :TkImage, 'tkcanvas' +autoload :TkBitmapImage, 'tkcanvas' +autoload :TkPhotoImage, 'tkcanvas' +autoload :TkEntry, 'tkentry' +autoload :TkText, 'tktext' +autoload :TkDialog, 'tkdialog' +autoload :TkMenubar, 'tkmenubar' +autoload :TkAfter, 'tkafter' +autoload :TkPalette, 'tkpalette' +autoload :TkFont, 'tkfont' +autoload :TkVirtualEvent, 'tkvirtevent' diff --git a/ext/tk/lib/tkafter.rb b/ext/tk/lib/tkafter.rb new file mode 100644 index 0000000000..be2e50ff3a --- /dev/null +++ b/ext/tk/lib/tkafter.rb @@ -0,0 +1,296 @@ +# +# tkafter.rb : methods for Tcl/Tk after command +# 1998/07/02 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp> +# +require 'tk' + +class TkAfter + include TkCore + extend TkCore + + Tk_CBID = [0] + Tk_CBTBL = {} + + INTERP._invoke("proc", "rb_after", "args", "ruby [format \"TkAfter.callback %%Q!%s!\" $args]") + + ############################### + # class methods + ############################### + def TkAfter.callback(arg) + @after_id = nil + arg = Array(tk_split_list(arg)) + obj_id = arg.shift + ex_obj = Tk_CBTBL[obj_id] + return nil if ex_obj == nil; # canceled + _get_eval_string(ex_obj.do_callback(*arg)) + end + + def TkAfter.info + tk_call('after', 'info').split(' ').filter{|id| + ret = Tk_CBTBL.find{|key,val| val.after_id == id} + (ret == nil)? id: ret[1] + } + end + + ############################### + # instance methods + ############################### + def do_callback(*args) + @in_callback = true + ret = @current_proc.call(*args) + if @set_next + set_next_callback(*args) + else + @set_next = true + end + @in_callback = false + ret + end + + def set_callback(sleep, args=nil) + @after_script = "rb_after #{@id} #{_get_eval_string(args)}" + @after_id = tk_call('after', sleep, @after_script) + @current_script = [sleep, @after_script] + end + + def set_next_callback(*args) + if @running == false || @proc_max == 0 || @do_loop == 0 + Tk_CBTBL[@id] = nil ;# for GC + @running = false + return + end + if @current_pos >= @proc_max + if @do_loop < 0 || (@do_loop -= 1) > 0 + @current_pos = 0 + else + Tk_CBTBL[@id] = nil ;# for GC + @running = false + return + end + end + + @current_args = args + + if @sleep_time.kind_of? Proc + sleep = @sleep_time.call(*args) + else + sleep = @sleep_time + end + @current_sleep = sleep + + cmd, *cmd_args = @loop_proc[@current_pos] + @current_pos += 1 + @current_proc = cmd + + if cmd_args[0].kind_of? Proc + #c = cmd_args.shift + #cb_args = c.call(*(cmd_args + args)) + cb_args = cmd_args[0].call(*args) + else + cb_args = cmd_args + end + + set_callback(sleep, cb_args) + end + + def initialize(*args) + @id = format("a%.4d", Tk_CBID[0]) + Tk_CBID[0] += 1 + + @set_next = true + + @init_sleep = 0 + @init_proc = nil + @init_args = [] + + @current_script = [] + @current_proc = nil + @current_args = nil + + @sleep_time = 0 + @current_sleep = 0 + @loop_exec = 0 + @do_loop = 0 + @loop_proc = [] + @proc_max = 0 + @current_pos = 0 + + @after_id = nil + @after_script = nil + + set_procs(*args) if args != [] + + @running = false + end + + attr :after_id + attr :after_script + attr :current_proc + attr :current_sleep + + attr_accessor :loop_exec + + def get_procs + [@init_sleep, @init_proc, @init_args, @sleep_time, @loop_exec, @loop_proc] + end + + def current_status + [@running, @current_sleep, @current_proc, @current_args, @do_loop] + end + + def running? + @running + end + + def loop_rest + @do_loop + end + + def loop_rest=(rest) + @do_loop = rest + end + + def set_procs(interval, loop_exec, *procs) + if !interval == 'idle' \ + && !interval.kind_of?(Integer) && !interval.kind_of?(Proc) + fail format("%s need to be Integer or Proc", interval.inspect) + end + @sleep_time = interval + + @loop_proc = [] + procs.each{|e| + if e.kind_of? Proc + @loop_proc.push([e]) + else + @loop_proc.push(e) + end + } + @proc_max = @loop_proc.size + @current_pos = 0 + + @do_loop = 0 + if loop_exec + if loop_exec.kind_of?(Integer) && loop_exec < 0 + @loop_exec = -1 + elsif loop_exec == nil || loop_exec == false || loop_exec == 0 + @loop_exec = 1 + else + if not loop_exec.kind_of?(Integer) + fail format("%s need to be Integer", loop_exec.inspect) + end + @loop_exec = loop_exec + end + @do_loop = @loop_exec + end + + self + end + + def add_procs(*procs) + procs.each{|e| + if e.kind_of? Proc + @loop_proc.push([e]) + else + @loop_proc.push(e) + end + } + @proc_max = @loop_proc.size + + self + end + + def set_start_proc(sleep, init_proc, *init_args) + if !sleep == 'idle' && !sleep.kind_of?(Integer) + fail format("%s need to be Integer", sleep.inspect) + end + @init_sleep = sleep + @init_proc = init_proc + @init_args = init_args + self + end + + def start(*init_args) + return nil if @running + + Tk_CBTBL[@id] = self + @do_loop = @loop_exec + @current_pos = 0 + + argc = init_args.size + if argc > 0 + sleep = init_args.shift + if !sleep == 'idle' && !sleep.kind_of?(Integer) + fail format("%s need to be Integer", sleep.inspect) + end + @init_sleep = sleep + end + @init_proc = init_args.shift if argc > 1 + @init_args = init_args if argc > 0 + + @current_sleep = @init_sleep + @running = true + if @init_proc + if not @init_proc.kind_of? Proc + fail format("%s need to be Proc", @init_proc.inspect) + end + @current_proc = @init_proc + set_callback(sleep, @init_args) + @set_next = false if @in_callback + else + set_next_callback(*@init_args) + end + + self + end + + def restart(*restart_args) + cancel if @running + if restart_args == [] + start(@init_sleep, @init_proc, *@init_args) + else + start(*restart_args) + end + end + + def cancel + @running = false + tk_call 'after', 'cancel', @after_id if @after_id + @after_id = nil + Tk_CBTBL[@id] = nil ;# for GC + self + end + alias stop cancel + + def continue(wait=nil) + sleep, cmd = @current_script + return nil if cmd == nil || @running == true + if wait + if not wait.kind_of? Integer + fail format("%s need to be Integer", wait.inspect) + end + sleep = wait + end + Tk_CBTBL[@id] = self + @running = true + @after_id = tk_call('after', sleep, cmd) + self + end + + def skip + return nil if @running == false + cancel + Tk_CBTBL[@id] = self + @running = true + set_next_callback(@current_args) + self + end + + def info + if @after_id + inf = tk_split_list(tk_call('after', 'info', @after_id)) + [Tk_CBTBL[inf[0][1]], inf[1]] + else + nil + end + end +end diff --git a/ext/tk/lib/tkbgerror.rb b/ext/tk/lib/tkbgerror.rb new file mode 100644 index 0000000000..8022077a3f --- /dev/null +++ b/ext/tk/lib/tkbgerror.rb @@ -0,0 +1,17 @@ +# +# tkbgerror -- bgerror ( tkerror ) module +# 1998/07/16 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp> +# +require 'tk' + +module TkBgError + extend Tk + + def bgerror(message) + tk_call 'bgerror', message + end + alias tkerror bgerror + alias show bgerror + + module_function :bgerror, :tkerror, :show +end diff --git a/ext/tk/lib/tkcanvas.rb b/ext/tk/lib/tkcanvas.rb new file mode 100644 index 0000000000..1cf24eeb7b --- /dev/null +++ b/ext/tk/lib/tkcanvas.rb @@ -0,0 +1,829 @@ +# +# tkcanvas.rb - Tk canvas classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> +# $Date$ +# by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp> + +require "tk" +require 'tkfont' + +module TkTreatCItemFont + def tagfont_configinfo(tagOrId) + if tagOrId.kind_of?(TkcItem) || tagOrId.kind_of?(TkcTag) + pathname = self.path + ';' + tagOrId.id.to_s + else + pathname = self.path + ';' + tagOrId.to_s + end + ret = TkFont.used_on(pathname) + if ret == nil + ret = TkFont.init_widget_font(pathname, + self.path, 'itemconfigure', tagOrId) + end + ret + end + alias tagfontobj tagfont_configinfo + + def tagfont_configure(tagOrId, slot) + if tagOrId.kind_of?(TkcItem) || tagOrId.kind_of?(TkcTag) + pathname = self.path + ';' + tagOrId.id.to_s + else + pathname = self.path + ';' + tagOrId.to_s + end + if (fnt = slot['font']) + slot['font'] = nil + if fnt.kind_of? TkFont + return fnt.call_font_configure(pathname, + self.path,'itemconfigure',tagOrId,slot) + else + latintagfont_configure(tagOrId, fnt) if fnt + end + end + if (ltn = slot['latinfont']) + slot['latinfont'] = nil + latintagfont_configure(tagOrId, ltn) if ltn + end + if (ltn = slot['asciifont']) + slot['asciifont'] = nil + latintagfont_configure(tagOrId, ltn) if ltn + end + if (knj = slot['kanjifont']) + slot['kanjifont'] = nil + kanjitagfont_configure(tagOrId, knj) if knj + end + + tk_call(self.path, 'itemconfigure', tagOrId, *hash_kv(slot)) if slot != {} + self + end + + def latintagfont_configure(tagOrId, ltn, keys=nil) + fobj = tagfontobj(tagOrId) + if ltn.kind_of? TkFont + conf = {} + ltn.latin_configinfo.each{|key,val| conf[key] = val if val != []} + if conf == {} + fobj.latin_replace(ltn) + fobj.latin_configure(keys) if keys + elsif keys + fobj.latin_configure(conf.update(keys)) + else + fobj.latin_configure(conf) + end + else + fobj.latin_replace(ltn) + end + end + alias asciitagfont_configure latintagfont_configure + + def kanjitagfont_configure(tagOrId, knj, keys=nil) + fobj = tagfontobj(tagOrId) + if knj.kind_of? TkFont + conf = {} + knj.kanji_configinfo.each{|key,val| conf[key] = val if val != []} + if conf == {} + fobj.kanji_replace(knj) + fobj.kanji_configure(keys) if keys + elsif keys + fobj.kanji_configure(conf.update(keys)) + else + fobj.kanji_configure(conf) + end + else + fobj.kanji_replace(knj) + end + end + + def tagfont_copy(tagOrId, window, wintag=nil) + if wintag + window.tagfontobj(wintag).configinfo.each{|key,value| + tagfontobj(tagOrId).configure(key,value) + } + tagfontobj(tagOrId).replace(window.tagfontobj(wintag).latin_font, + window.tagfontobj(wintag).kanji_font) + else + window.tagfont(tagOrId).configinfo.each{|key,value| + tagfontobj(tagOrId).configure(key,value) + } + tagfontobj(tagOrId).replace(window.fontobj.latin_font, + window.fontobj.kanji_font) + end + end + + def latintagfont_copy(tagOrId, window, wintag=nil) + if wintag + tagfontobj(tagOrId).latin_replace(window.tagfontobj(wintag).latin_font) + else + tagfontobj(tagOrId).latin_replace(window.fontobj.latin_font) + end + end + alias asciitagfont_copy latintagfont_copy + + def kanjitagfont_copy(tagOrId, window, wintag=nil) + if wintag + tagfontobj(tagOrId).kanji_replace(window.tagfontobj(wintag).kanji_font) + else + tagfontobj(tagOrId).kanji_replace(window.fontobj.kanji_font) + end + end +end + +class TkCanvas<TkWindow + include TkTreatCItemFont + + WidgetClassName = 'Canvas'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + + def create_self + tk_call 'canvas', path + end + + def tagid(tag) + if tag.kind_of?(TkcItem) || tag.kind_of?(TkcTag) + tag.id + else + tag + end + end + private :tagid + + def addtag(tag, mode, *args) + tk_send 'addtag', tagid(tag), mode, *args + end + def addtag_above(tagOrId, target) + addtag(tagOrId, 'above', tagid(target)) + end + def addtag_all(tagOrId) + addtag(tagOrId, 'all') + end + def addtag_below(tagOrId, target) + addtag(tagOrId, 'below', tagid(target)) + end + def addtag_closest(tagOrId, x, y, halo=None, start=None) + addtag(tagOrId, 'closest', x, y, halo, start) + end + def addtag_enclosed(tagOrId, x1, y1, x2, y2) + addtag(tagOrId, 'enclosed', x1, y1, x2, y2) + end + def addtag_overlapping(tagOrId, x1, y1, x2, y2) + addtag(tagOrId, 'overlapping', x1, y1, x2, y2) + end + def addtag_withtag(tagOrId, tag) + addtag(tagOrId, 'withtag', tagid(tag)) + end + + def bbox(tagOrId, *tags) + list(tk_send('bbox', tagid(tagOrId), *tags)) + end + + def itembind(tag, context, cmd=Proc.new, args=nil) + id = install_bind(cmd, args) + begin + tk_send 'bind', tagid(tag), "<#{tk_event_sequence(context)}>", id + rescue + uninstall_cmd(cmd) + fail + end + # @cmdtbl.push id + end + + def itembindinfo(tag, context=nil) + if context + (tk_send('bind', tagid(tag), + "<#{tk_event_sequence(context)}>")).collect{|cmdline| + if cmdline =~ /^rb_out (c\d+)\s+(.*)$/ + [Tk_CMDTBL[$1], $2] + else + cmdline + end + } + else + tk_split_list(tk_send 'bind', tagid(tag)).filter{|seq| + seq[1..-2].gsub(/></,',') + } + end + end + + def canvasx(x, *args) + tk_tcl2ruby(tk_send 'canvasx', x, *args) + end + def canvasy(y, *args) + tk_tcl2ruby(tk_send 'canvasy', y, *args) + end + + def coords(tag, *args) + if args == [] + tk_split_list(tk_send('coords', tagid(tag))) + else + tk_send('coords', tagid(tag), *args) + end + end + + def dchars(tag, first, last=None) + tk_send 'dchars', tagid(tag), first, last + end + + def delete(*args) + tk_send 'delete', *args + end + alias remove delete + + def dtag(tag, tag_to_del=None) + tk_send 'dtag', tagid(tag), tag_to_del + end + + def find(mode, *args) + list(tk_send 'find', mode, *args).filter{|id| + TkcItem.id2obj(id) + } + end + def find_above(target) + find('above', tagid(target)) + end + def find_all + find('all') + end + def find_below(target) + find('below', tagid(target)) + end + def find_closest(x, y, halo=None, start=None) + find('closest', x, y, halo, start) + end + def find_enclosed(x1, y1, x2, y2) + find('enclosed', x1, y1, x2, y2) + end + def find_overlapping(x1, y1, x2, y2) + find('overlapping', x1, y1, x2, y2) + end + def find_withtag(tag) + find('withtag', tag) + end + + def itemfocus(tagOrId=nil) + if tagOrId + tk_send 'focus', tagid(tagOrId) + else + ret = tk_send('focus') + if ret == "" + nil + else + TkcItem.id2obj(ret) + end + end + end + + def gettags(tagOrId) + list(tk_send('gettags', tagid(tagOrId))).collect{|tag| + TkcTag.id2obj(tag) + } + end + + def icursor(tagOrId, index) + tk_send 'icursor', tagid(tagOrId), index + end + + def index(tagOrId, index) + tk_send 'index', tagid(tagOrId), index + end + + def insert(tagOrId, index, string) + tk_send 'insert', tagid(tagOrId), index, string + end + + def itemcget(tagOrId, option) + tk_tcl2ruby tk_send 'itemcget', tagid(tagOrId), "-#{option}" + end + + def itemconfigure(tagOrId, key, value=None) + if key.kind_of? Hash + if ( key['font'] || key['kanjifont'] \ + || key['latinfont'] || key['asciifont'] ) + tagfont_configure(tagOrId, key.dup) + else + tk_send 'itemconfigure', tagid(tagOrId), *hash_kv(key) + end + + else + if ( key == 'font' || key == 'kanjifont' \ + || key == 'latinfont' || key == 'asciifont' ) + tagfont_configure(tagid(tagOrId), {key=>value}) + else + tk_send 'itemconfigure', tagid(tagOrId), "-#{key}", value + end + end + end +# def itemconfigure(tagOrId, key, value=None) +# if key.kind_of? Hash +# tk_send 'itemconfigure', tagid(tagOrId), *hash_kv(key) +# else +# tk_send 'itemconfigure', tagid(tagOrId), "-#{key}", value +# end +# end +# def itemconfigure(tagOrId, keys) +# tk_send 'itemconfigure', tagid(tagOrId), *hash_kv(keys) +# end + + def itemconfiginfo(tagOrId, key=nil) + if key + conf = tk_split_list(tk_send 'itemconfigure', tagid(tagOrId), "-#{key}") + conf[0] = conf[0][1..-1] + conf + else + tk_split_list(tk_send 'itemconfigure', tagid(tagOrId)).collect{|conf| + conf[0] = conf[0][1..-1] + conf + } + end + end + + def lower(tag, below=None) + tk_send 'lower', tagid(tag), below + end + + def move(tag, x, y) + tk_send 'move', tagid(tag), x, y + end + + def postscript(keys) + tk_send "postscript", *hash_kv(keys) + end + + def raise(tag, above=None) + tk_send 'raise', tagid(tag), above + end + + def scale(tag, x, y, xs, ys) + tk_send 'scale', tagid(tag), x, y, xs, ys + end + + def scan_mark(x, y) + tk_send 'scan', 'mark', x, y + end + def scan_dragto(x, y) + tk_send 'scan', 'dragto', x, y + end + + def select(mode, *args) + tk_send 'select', mode, *args + end + def select_adjust(tagOrId, index) + select('adjust', tagid(tagOrId), index) + end + def select_clear + select('clear') + end + def select_from(tagOrId, index) + select('from', tagid(tagOrId), index) + end + def select_item + select('item') + end + def select_to(tagOrId, index) + select('to', tagid(tagOrId), index) + end + + def itemtype(tag) + TkcItem.type2class(tk_send 'type', tagid(tag)) + end + + def xview(*index) + tk_send 'xview', *index + end + def yview(*index) + tk_send 'yview', *index + end +end + +module TkcTagAccess + include TkComm + include TkTreatTagFont + + def addtag(tag) + @c.addtag(tag, 'with', @id) + end + + def bbox + @c.bbox(@id) + end + + def bind(seq, cmd=Proc.new, args=nil) + @c.itembind @id, seq, cmd, args + end + + def bindinfo(seq=nil) + @c.itembindinfo @id, seq + end + + def cget(option) + @c.itemcget @id, option + end + + def configure(key, value=None) + @c.itemconfigure @id, key, value + end +# def configure(keys) +# @c.itemconfigure @id, keys +# end + + def configinfo(key=nil) + @c.itemconfiginfo @id, key + end + + def coords(*args) + @c.coords @id, *args + end + + def dchars(first, last=None) + @c.dchars @id, first, last + end + + def dtag(tag_to_del=None) + @c.dtag @id, tag_to_del + end + + def find + @c.find 'withtag', @id + end + alias list find + + def focus + @c.itemfocus @id + end + + def gettags + @c.gettags @id + end + + def icursor(index) + @c.icursor @id, index + end + + def index(index) + @c.index @id, index + end + + def insert(beforethis, string) + @c.insert @id, beforethis, string + end + + def lower(belowthis=None) + @c.lower @id, belowthis + end + + def move(xamount, yamount) + @c.move @id, xamount, yamount + end + + def raise(abovethis=None) + @c.raise @id, abovethis + end + + def scale(xorigin, yorigin, xscale, yscale) + @c.scale @id, xorigin, yorigin, xscale, yscale + end + + def select_adjust(index) + @c.select('adjust', @id, index) + end + def select_from(index) + @c.select('from', @id, index) + end + def select_to(index) + @c.select('to', @id, index) + end + + def itemtype + @c.itemtype @id + end +end + +class TkcTag<TkObject + include TkcTagAccess + + CTagID_TBL = {} + + def TkcTag.id2obj(id) + CTagID_TBL[id]? CTagID_TBL[id]: id + end + + $tk_canvas_tag = 'ctag0000' + def initialize(parent, mode=nil, *args) + if not parent.kind_of?(TkCanvas) + fail format("%s need to be TkCanvas", parent.inspect) + end + @c = parent + @path = @id = $tk_canvas_tag + CTagID_TBL[@id] = self + $tk_canvas_tag = $tk_canvas_tag.succ + if mode + tk_call @c.path, "addtag", @id, mode, *args + end + end + def id + return @id + end + + def delete + @c.delete @id + CTagID_TBL[@id] = nil + end + alias remove delete + alias destroy delete + + def set_to_above(target) + @c.addtag_above(@id, target) + end + alias above set_to_above + + def set_to_all + @c.addtag_all(@id) + end + alias all set_to_all + + def set_to_below(target) + @c.addtag_below(@id, target) + end + alias below set_to_below + + def set_to_closest(x, y, halo=None, start=None) + @c.addtag_closest(@id, x, y, halo, start) + end + alias closest set_to_closest + + def set_to_enclosed(x1, y1, x2, y2) + @c.addtag_enclosest(@id, x1, y1, x2, y2) + end + alias enclosed set_to_enclosed + + def set_to_overlapping(x1, y1, x2, y2) + @c.addtag_overlapping(@id, x1, y1, x2, y2) + end + alias overlapping set_to_overlapping + + def set_to_withtag(target) + @c.addtag_withtag(@id, target) + end + alias withtag set_to_withtag +end + +class TkcTagAll<TkcTag + def initialize(parent) + if not parent.kind_of?(TkCanvas) + fail format("%s need to be TkCanvas", parent.inspect) + end + @c = parent + @path = @id = 'all' + CTagID_TBL[@id] = self + end +end + +class TkcTagCurrent<TkcTag + def initialize(parent) + if not parent.kind_of?(TkCanvas) + fail format("%s need to be TkCanvas", parent.inspect) + end + @c = parent + @path = @id = 'current' + CTagID_TBL[@id] = self + end +end + +class TkcGroup<TkcTag + $tk_group_id = 'tkg00000' + def create_self(parent, *args) + if not parent.kind_of?(TkCanvas) + fail format("%s need to be TkCanvas", parent.inspect) + end + @c = parent + @path = @id = $tk_group_id + CTagID_TBL[@id] = self + $tk_group_id = $tk_group_id.succ + add(*args) if args != [] + end + + def include(*tags) + for i in tags + i.addtag @id + end + end + + def exclude(*tags) + for i in tags + i.delete @id + end + end +end + + +class TkcItem<TkObject + include TkcTagAccess + + CItemTypeToClass = {} + CItemID_TBL = {} + + def TkcItem.type2class(type) + CItemTypeToClass[type] + end + + def TkcItem.id2obj(id) + CItemID_TBL[id]? CItemID_TBL[id]: id + end + + def initialize(parent, *args) + if not parent.kind_of?(TkCanvas) + fail format("%s need to be TkCanvas", parent.inspect) + end + @parent = @c = parent + @path = parent.path + if args[-1].kind_of? Hash + keys = args.pop + end + @id = create_self(*args).to_i ;# 'canvas item id' is integer number + CItemID_TBL[@id] = self + if keys + # tk_call @path, 'itemconfigure', @id, *hash_kv(keys) + configure(keys) if keys + end + end + def create_self(*args); end + private :create_self + def id + return @id + end + + def delete + @c.delete @id + CItemID_TBL[@id] = nil + end + alias remove delete + alias destroy delete +end + +class TkcArc<TkcItem + CItemTypeToClass['arc'] = self + def create_self(*args) + tk_call(@path, 'create', 'arc', *args) + end +end +class TkcBitmap<TkcItem + CItemTypeToClass['bitmap'] = self + def create_self(*args) + tk_call(@path, 'create', 'bitmap', *args) + end +end +class TkcImage<TkcItem + CItemTypeToClass['image'] = self + def create_self(*args) + tk_call(@path, 'create', 'image', *args) + end +end +class TkcLine<TkcItem + CItemTypeToClass['line'] = self + def create_self(*args) + tk_call(@path, 'create', 'line', *args) + end +end +class TkcOval<TkcItem + CItemTypeToClass['oval'] = self + def create_self(*args) + tk_call(@path, 'create', 'oval', *args) + end +end +class TkcPolygon<TkcItem + CItemTypeToClass['polygon'] = self + def create_self(*args) + tk_call(@path, 'create', 'polygon', *args) + end +end +class TkcRectangle<TkcItem + CItemTypeToClass['rectangle'] = self + def create_self(*args) + tk_call(@path, 'create', 'rectangle', *args) + end +end +class TkcText<TkcItem + CItemTypeToClass['text'] = self + def create_self(*args) + tk_call(@path, 'create', 'text', *args) + end +end +class TkcWindow<TkcItem + CItemTypeToClass['window'] = self + def create_self(*args) + tk_call(@path, 'create', 'window', *args) + end +end + +class TkImage<TkObject + include Tk + + Tk_IMGTBL = {} + + $tk_image_id = 'i00000' + def initialize(keys=nil) + @path = $tk_image_id + $tk_image_id = $tk_image_id.succ + tk_call 'image', 'create', @type, @path, *hash_kv(keys) + Tk_IMGTBL[@path] = self + end + + def delete + Tk_IMGTBL[@id] = nil if @id + tk_call('image', 'delete', @path) + end + def height + number(tk_call('image', 'height', @path)) + end + def itemtype + tk_call('image', 'type', @path) + end + def width + number(tk_call('image', 'width', @path)) + end + + def TkImage.names + Tk.tk_call('image', 'names').split.filter{|id| + (Tk_IMGTBL[id])? Tk_IMGTBL[id] : id + } + end + + def TkImage.types + Tk.tk_call('image', 'types').split + end +end + +class TkBitmapImage<TkImage + def initialize(*args) + @type = 'bitmap' + super + end +end + +class TkPhotoImage<TkImage + def initialize(*args) + @type = 'photo' + super + end + + def blank + tk_send 'blank' + end + + def cget(option) + tk_tcl2ruby tk_send 'cget', option + end + + def copy(source, *opts) + args = opts.collect{|term| + if term.kind_of?(String) && term.include?(?\s) + term.split + else + term + end + }.flatten + + tk_send 'copy', source, *args + end + + def get(x, y) + tk_send 'get', x, y + end + + def put(data, *to) + if to == [] + tk_send 'put', data + else + tk_send 'put', data, '-to', *to + end + end + + def read(file, *opts) + args = opts.collect{|term| + if term.kind_of?(String) && term.include?(?\s) + term.split + else + term + end + }.flatten + + tk_send 'read', file, *args + end + + def redither + tk_send 'redither' + end + + def write(file, *opts) + args = opts.collect{|term| + if term.kind_of?(String) && term.include?(?\s) + term.split + else + term + end + }.flatten + + tk_send 'write', file, *args + end +end diff --git a/ext/tk/lib/tkclass.rb b/ext/tk/lib/tkclass.rb new file mode 100644 index 0000000000..0b33d4ec8b --- /dev/null +++ b/ext/tk/lib/tkclass.rb @@ -0,0 +1,38 @@ +# +# tkclass.rb - Tk classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require "tk" + +TopLevel = TkToplevel +Frame = TkFrame +Label = TkLabel +Button = TkButton +Radiobutton = TkRadioButton +Checkbutton = TkCheckButton +Message = TkMessage +Entry = TkEntry +Text = TkText +Scale = TkScale +Scrollbar = TkScrollbar +Listbox = TkListbox +Menu = TkMenu +Menubutton = TkMenubutton +Canvas = TkCanvas +Arc = TkcArc +Bitmap = TkcBitmap +Line = TkcLine +Oval = TkcOval +Polygon = TkcPolygon +Rectangle = TkcRectangle +TextItem = TkcText +WindowItem = TkcWindow +Selection = TkSelection +Winfo = TkWinfo +Pack = TkPack +Variable = TkVariable + +def Mainloop + Tk.mainloop +end diff --git a/ext/tk/lib/tkdialog.rb b/ext/tk/lib/tkdialog.rb new file mode 100644 index 0000000000..1133db6ae9 --- /dev/null +++ b/ext/tk/lib/tkdialog.rb @@ -0,0 +1,141 @@ +require "tk" + +class TkDialog < TkWindow + extend Tk + + # initialize tk_dialog + def initialize(keys = nil) + super() + @var = TkVariable.new + id = @var.id + + @title = title + + @message = message + @message_config = message_config + + @bitmap = bitmap + @bitmap_config = message_config + + @default_button = default_button + + @buttons = buttons + @button_configs = proc{|num| button_configs num} + + if keys.kind_of? Hash + @title = keys['title'] if keys['title'] + @message = keys['message'] if keys['message'] + @bitmap = keys['bitmap'] if keys['bitmap'] + @default_button = keys['default'] if keys['default'] + @buttons = keys['buttons'] if keys['buttons'] + + @command = keys['prev_command'] + + @message_config = keys['message_config'] if keys['message_config'] + @bitmap_config = keys['bitmap_config'] if keys['bitmap_config'] + @button_configs = keys['button_configs'] if keys['button_configs'] + end + + if @title.include? ?\s + @title = '{' + @title + '}' + end + + @buttons = tk_split_list(@buttons) if @buttons.kind_of? String + @buttons = @buttons.collect{|s| + if s.kind_of? Array + s = s.join(' ') + end + if s.include? ?\s + '{' + s + '}' + else + s + end + } + + config = "" + if @message_config.kind_of? Hash + config << format("%s.msg configure %s\n", + @path, hash_kv(@message_config).join(' ')) + end + if @bitmap_config.kind_of? Hash + config << format("%s.msg configure %s\n", + @path, hash_kv(@bitmap_config).join(' ')) + end + if @button_configs.kind_of? Proc + @buttons.each_index{|i| + if (c = @button_configs.call(i)).kind_of? Hash + config << format("%s.button%s configure %s\n", + @path, i, hash_kv(c).join(' ')) + end + } + end + config = 'after idle {' + config + '};' if config != "" + + if @command.kind_of? Proc + @command.call(self) + end + + INTERP._eval('eval {global '+id+';'+config+ + 'set '+id+' [tk_dialog '+ + @path+" "+@title+" {#{@message}} "+@bitmap+" "+ + String(@default_button)+" "+@buttons.join(' ')+']}') + end + def value + return @var.value.to_i + end + ###################################################### + # # + # these methods must be overridden for each dialog # + # # + ###################################################### + def title + return "DIALOG" + end + def message + return "MESSAGE" + end + def message_config + return nil + end + def bitmap + return "info" + end + def bitmap_config + return nil + end + def default_button + return 0 + end + def buttons + #return "BUTTON1 BUTTON2" + return ["BUTTON1", "BUTTON2"] + end + def button_configs(num) + return nil + end +end + +# +# dialog for warning +# +class TkWarning < TkDialog + def initialize(mes) + @mes = mes + super() + end + def message + return @mes + end + def title + return "WARNING"; + end + def bitmap + return "warning"; + end + def default_button + return 0; + end + def buttons + return "OK"; + end +end diff --git a/ext/tk/lib/tkentry.rb b/ext/tk/lib/tkentry.rb new file mode 100644 index 0000000000..b834c455c6 --- /dev/null +++ b/ext/tk/lib/tkentry.rb @@ -0,0 +1,73 @@ +# +# tkentry.rb - Tk entry classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require 'tk.rb' + +class TkEntry<TkLabel + WidgetClassName = 'Entry'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + + def create_self + tk_call 'entry', @path + end + def scrollcommand(cmd) + configure 'scrollcommand', cmd + end + + def delete(s, e=None) + tk_send 'delete', s, e + end + + def cursor + tk_send 'index', 'insert' + end + def cursor=(index) + tk_send 'icursor', index + end + def index(index) + number(tk_send('index', index)) + end + def insert(pos,text) + tk_send 'insert', pos, text + end + def mark(pos) + tk_send 'scan', 'mark', pos + end + def dragto(pos) + tk_send 'scan', 'dragto', pos + end + def selection_adjust(index) + tk_send 'selection', 'adjust', index + end + def selection_clear + tk_send 'selection', 'clear', 'end' + end + def selection_from(index) + tk_send 'selection', 'from', index + end + def selection_present() + tk_send('selection', 'present') == 1 + end + def selection_range(s, e) + tk_send 'selection', 'range', s, e + end + def selection_to(index) + tk_send 'selection', 'to', index + end + def xview(*index) + tk_send 'xview', *index + end + + def value + tk_send 'get' + end + def value= (val) + tk_send 'delete', 0, 'end' + tk_send 'insert', 0, val + end +end diff --git a/ext/tk/lib/tkfont.rb b/ext/tk/lib/tkfont.rb new file mode 100644 index 0000000000..421988aeca --- /dev/null +++ b/ext/tk/lib/tkfont.rb @@ -0,0 +1,944 @@ +# +# tkfont.rb - the class to treat fonts on Ruby/Tk +# +# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp) +# +require 'tk' + +class TkFont + include Tk + extend TkCore + + Tk_FontID = [0] + Tk_FontNameTBL = {} + Tk_FontUseTBL = {} + + DEFAULT_LATIN_FONT_NAME = 'a14'.freeze + DEFAULT_KANJI_FONT_NAME = 'k14'.freeze + + ################################### + # class methods + ################################### + def TkFont.families(window=nil) + case (Tk::TK_VERSION) + when /^4\.*/ + ['fixed'] + + when /^8\.*/ + if window + tk_split_simplelist(tk_call('font', 'families', '-displayof', window)) + else + tk_split_simplelist(tk_call('font', 'families')) + end + end + end + + def TkFont.names + case (Tk::TK_VERSION) + when /^4\.*/ + r = ['fixed'] + r += ['a14', 'k14'] if JAPANIZED_TK + Tk_FontNameTBL.each_value{|obj| r.push(obj)} + r | [] + + when /^8\.*/ + tk_split_simplelist(tk_call('font', 'names')) + + end + end + + def TkFont.create_copy(font) + keys = {} + font.configure.each{|key,value| keys[key] = value } + new_font = TkFont.new(font.latin_font, font.kanji_font, keys) + end + + def TkFont.get_obj(name) + if name =~ /^(@font[0-9]+)(|c|l|k)$/ + Tk_FontNameTBL[$1] + else + nil + end + end + + def TkFont.init_widget_font(path, *args) + case (Tk::TK_VERSION) + when /^4\.*/ + conf = tk_split_simplelist(tk_call(*args)). + find_all{|prop| prop[0..5]=='-font ' || prop[0..10]=='-kanjifont '}. + collect{|prop| tk_split_simplelist(prop)} + if font_inf = conf.assoc('-font') + ltn = font_inf[4] + ltn = nil if ltn == [] + else + #ltn = nil + raise RuntimeError, "unknown option '-font'" + end + if font_inf = conf.assoc('-kanjifont') + knj = font_inf[4] + knj = nil if knj == [] + else + knj = nil + end + TkFont.new(ltn, knj).call_font_configure(path, *(args + [{}])) + + when /^8\.*/ + font_prop = tk_split_simplelist(tk_call(*args)).find{|prop| + prop[0..5] == '-font ' + } + unless font_prop + raise RuntimeError, "unknown option '-font'" + end + fnt = tk_split_simplelist(font_prop)[4] + if fnt == "" + TkFont.new(nil, nil).call_font_configure(path, *(args + [{}])) + else + begin + compound = Hash[*list(tk_call('font', 'configure', + fnt))].collect{|key,value| + [key[1..-1], value] + }.assoc('compound')[1] + rescue + compound = [] + end + if compound == [] + TkFont.new(fnt, DEFAULT_KANJI_FONT_NAME) \ + .call_font_configure(path, *(args + [{}])) + else + TkFont.new(compound[0], compound[1]) \ + .call_font_configure(path, *(args + [{}])) + end + end + end + end + + def TkFont.used_on(path=nil) + if path + Tk_FontUseTBL[path] + else + Tk_FontUseTBL.values | [] + end + end + + ################################### + private + ################################### + def initialize(ltn=nil, knj=nil, keys=nil) + @id = format("@font%.4d", Tk_FontID[0]) + Tk_FontID[0] += 1 + Tk_FontNameTBL[@id] = self + + ltn = DEFAULT_LATIN_FONT_NAME unless ltn + create_latinfont(ltn) + + knj = DEFAULT_KANJI_FONT_NAME unless knj + create_kanjifont(knj) + + create_compoundfont(keys) + end + + def _get_font_info_from_hash(font) + foundry = (info = font['foundry'] .to_s)? info: '*' + family = (info = font['family'] .to_s)? info: '*' + weight = (info = font['weight'] .to_s)? info: '*' + slant = (info = font['slant'] .to_s)? info: '*' + swidth = (info = font['swidth'] .to_s)? info: '*' + adstyle = (info = font['adstyle'] .to_s)? info: '*' + pixels = (info = font['pixels'] .to_s)? info: '*' + points = (info = font['points'] .to_s)? info: '*' + resx = (info = font['resx'] .to_s)? info: '*' + resy = (info = font['resy'] .to_s)? info: '*' + space = (info = font['space'] .to_s)? info: '*' + avgWidth = (info = font['avgWidth'].to_s)? info: '*' + charset = (info = font['charset'] .to_s)? info: '*' + encoding = (info = font['encoding'].to_s)? info: '*' + + Array([foundry, family, weight, slant, swidth, adstyle, + pixels, points, resx, resy, space, avgWidth, charset, encoding]) + end + + def create_latinfont_tk4x(font) + if font.kind_of? Hash + @latinfont = '-' + _get_font_info_from_hash(font).join('-') + '-' + + elsif font.kind_of? Array + finfo = {} + finfo['family'] = font[0].to_s + if font[1] + fsize = font[1].to_s + if fsize != '0' && fsize =~ /^(|\+|-)([0-9]+)$/ + if $1 == '-' + finfo['pixels'] = $2 + else + finfo['points'] = $2 + end + else + finfo['points'] = '13' + end + end + font[2..-1].each{|style| + case (style) + when 'normal' + finfo['weight'] = style + when 'bold' + finfo['weight'] = style + when 'roman' + finfo['slant'] = 'r' + when 'italic' + finfo['slant'] = 'i' + end + } + + @latinfont = '-' + _get_font_info_from_hash(finfo).join('-') + '-' + + elsif font.kind_of? TkFont + @latinfont = font.latin_font + + else + @latinfont = font + + end + end + + def create_kanjifont_tk4x(font) + unless JAPANIZED_TK + @kanjifont = "" + return + end + + if font.kind_of? Hash + @kanjifont = '-' + _get_font_info_from_hash(font).join('-') + '-' + + elsif font.kind_of? Array + finfo = {} + finfo['family'] = font[0].to_s + if font[1] + fsize = font[1].to_s + if fsize != '0' && fsize =~ /^(|\+|-)([0-9]+)$/ + if $1 == '-' + finfo['pixels'] = $2 + else + finfo['points'] = $2 + end + else + finfo['points'] = '13' + end + end + font[2..-1].each{|style| + case (style) + when 'normal' + finfo['weight'] = style + when 'bold' + finfo['weight'] = style + when 'roman' + finfo['slant'] = 'r' + when 'italic' + finfo['slant'] = 'i' + end + } + + @kanjifont = '-' + _get_font_info_from_hash(finfo).join('-') + '-' + + elsif font.kind_of? TkFont + @kanjifont = font.kanji_font + + else + @kanjifont = font + + end + end + + def create_compoundfont_tk4x(keys) + if JAPANIZED_TK + @compoundfont = [[@latinfont], [@kanjifont]] + @fontslot = {'font'=>@latinfont, 'kanjifont'=>@kanjifont} + else + @compoundfont = @latinfont + @fontslot = {'font'=>@latinfont} + end + end + + def create_latinfont_tk8x(font) + @latinfont = @id + 'l' + + if JAPANIZED_TK + if font.kind_of? Hash + tk_call('font', 'create', @latinfont, *hash_kv(font)) + elsif font.kind_of? Array + tk_call('font', 'create', @latinfont, '-copy', array2tk_list(font)) + elsif font.kind_of? TkFont + tk_call('font', 'create', @latinfont, '-copy', font.latin_font) + else + tk_call('font', 'create', @latinfont, '-copy', font) + end + else + if font.kind_of? Hash + tk_call('font', 'create', @latinfont, *hash_kv(font)) + else + keys = {} + if font.kind_of? Array + actual_core(array2tk_list(font)).each{|key,val| keys[key] = val} + elsif font.kind_of? TkFont + actual_core(font.latin_font).each{|key,val| keys[key] = val} + else + actual_core(font).each{|key,val| keys[key] = val} + end + tk_call('font', 'create', @latinfont, *hash_kv(keys)) + end + end + end + + def create_kanjifont_tk80(font) + unless JAPANIZED_TK + @kanjifont = "" + return + end + + @kanjifont = @id + 'k' + + if font.kind_of? Hash + if font['charset'] + tk_call('font', 'create', @kanjifont, *hash_kv(font)) + else + tk_call('font', 'create', @kanjifont, + '-charset', 'jisx0208.1983', *hash_kv(font)) + end + elsif font.kind_of? Array + tk_call('font', 'create', @kanjifont, '-copy', array2tk_list(font)) + tk_call('font', 'configure', @kanjifont, '-charset', 'jisx0208.1983') + + elsif font.kind_of? TkFont + tk_call('font', 'create', @kanjifont, '-copy', font.kanji_font) + + else + tk_call('font', 'create', @kanjifont, '-copy', font, + '-charset', 'jisx0208.1983') + + end + end + + def create_kanjifont_tk81(font) + @kanjifont = @id + 'k' + + if font.kind_of? Hash + tk_call('font', 'create', @kanjifont, *hash_kv(font)) + else + keys = {} + if font.kind_of? Array + actual_core(array2tk_list(font)).each{|key,val| keys[key] = val} + elsif font.kind_of? TkFont + actual_core(font.kanji_font).each{|key,val| keys[key] = val} + else + actual_core(font).each{|key,val| keys[key] = val} + end + tk_call('font', 'create', @kanjifont, *hash_kv(keys)) + end + + keys = {} + actual_core(@kanjifont).each{|key,val| keys[key] = val} + begin + tk_call('font', 'configure', @compoundfont, *hash_kv(keys)) + rescue + end + end + + def create_compoundfont_tk80(keys) + @compoundfont = @id + 'c' + if JAPANIZED_TK + @fontslot = {'font'=>@compoundfont} + tk_call('font', 'create', @compoundfont, + '-compound', [@latinfont, @kanjifont], *hash_kv(keys)) + else + tk_call('font', 'create', @compoundfont) + latinkeys = {} + begin + actual_core(@latinfont).each{|key,val| latinkeys[key] = val} + rescue + latinkeys {} + end + if latinkeys != {} + tk_call('font', 'configure', @compoundfont, *hash_kv(latinkeys)) + end + @fontslot = {'font'=>@compoundfont} + tk_call('font', 'configure', @compoundfont, *hash_kv(keys)) + end + end + + def create_compoundfont_tk81(keys) + @compoundfont = @id + 'c' + tk_call('font', 'create', @compoundfont) + + latinkeys = {} + begin + actual_core(@latinfont).each{|key,val| latinkeys[key] = val} + rescue + latinkeys {} + end + if latinkeys != {} + tk_call('font', 'configure', @compoundfont, *hash_kv(latinkeys)) + end + + kanjikeys = {} + begin + actual_core(@kanjifont).each{|key,val| kanjikeys[key] = val} + rescue + kanjikeys {} + end + if kanjikeys != {} + tk_call('font', 'configure', @compoundfont, *hash_kv(kanjikeys)) + end + + @fontslot = {'font'=>@compoundfont} + tk_call('font', 'configure', @compoundfont, *hash_kv(keys)) + end + + def actual_core_tk4x(font, window=nil, option=nil) + # dummy + if option + "" + else + Array([ ['family',[]], ['size',[]], ['weight',[]], ['slant',[]], + ['underline',[]], ['overstrike',[]], ['charset',[]], + ['pointadjust',[]] ]) + end + end + + def actual_core_tk8x(font, window=nil, option=nil) + if option == 'compound' + "" + elsif option + if window + tk_call('font', 'actual', font, "-#{option}") + else + tk_call('font', 'actual', font, "-displayof", window, "-#{option}") + end + else + l = tk_split_simplelist(if window + tk_call('font', 'actual', font, + "-displayof", window) + else + tk_call('font', 'actual', font) + end) + r = [] + while key=l.shift + if key == '-compound' + l.shift + else + r.push [key[1..-1], l.shift] + end + end + r + end + end + + def configure_core_tk4x(font, slot, value=None) + "" + end + + def configinfo_core_tk4x(font, option=nil) + # dummy + if option + "" + else + Array([ ['family',[]], ['size',[]], ['weight',[]], ['slant',[]], + ['underline',[]], ['overstrike',[]], ['charset',[]], + ['pointadjust',[]] ]) + end + end + + def configure_core_tk8x(font, slot, value=None) + if slot.kind_of? Hash + tk_call 'font', 'configure', font, *hash_kv(slot) + else + tk_call 'font', 'configure', font, "-#{slot}", value + end + end + + def configinfo_core_tk8x(font, option=nil) + if option == 'compound' + "" + elsif option + tk_call('font', 'configure', font, "-#{option}") + else + l = tk_split_simplelist(tk_call('font', 'configure', font)) + r = [] + while key=l.shift + if key == '-compound' + l.shift + else + r.push [key[1..-1], l.shift] + end + end + r + end + end + + def delete_core_tk4x + Tk_FontNameTBL[@id] = nil + Tk_FontUseTBL.delete_if{|key,value| value == self} + end + + def delete_core_tk8x + begin + tk_call('font', 'delete', @latinfont) + rescue + end + begin + tk_call('font', 'delete', @kanjifont) + rescue + end + begin + tk_call('font', 'delete', @compoundfont) + rescue + end + Tk_FontNameTBL[@id] = nil + Tk_FontUseTBL.delete_if{|key,value| value == self} + end + + def latin_replace_core_tk4x(ltn) + create_latinfont_tk4x(ltn) + @compoundfont[0] = [@latinfont] if JAPANIZED_TK + @fontslot['font'] = @latinfont + Tk_FontUseTBL.dup.each{|w, fobj| + if self == fobj + begin + if w.include?(';') + win, tag = w.split(';') + winobj = tk_tcl2ruby(win) +# winobj.tagfont_configure(tag, {'font'=>@latinfont}) + if winobj.kind_of? TkText + tk_call(win, 'tag', 'configure', tag, '-font', @latinfont) + elsif winobj.kind_of? TkCanvas + tk_call(win, 'itemconfigure', tag, '-font', @latinfont) + elsif winobj.kind_of? TkMenu + tk_call(win, 'entryconfigure', tag, '-font', @latinfont) + else + raise RuntimeError, "unknown widget type" + end + else +# tk_tcl2ruby(w).font_configure('font'=>@latinfont) + tk_call(w, 'configure', '-font', @latinfont) + end + rescue + Tk_FontUseTBL[w] = nil + end + end + } + self + end + + def kanji_replace_core_tk4x(knj) + return self unless JAPANIZED_TK + + create_kanjifont_tk4x(knj) + @compoundfont[1] = [@kanjifont] + @fontslot['kanjifont'] = @kanjifont + Tk_FontUseTBL.dup.each{|w, fobj| + if self == fobj + begin + if w.include?(';') + win, tag = w.split(';') + winobj = tk_tcl2ruby(win) +# winobj.tagfont_configure(tag, {'kanjifont'=>@kanjifont}) + if winobj.kind_of? TkText + tk_call(win, 'tag', 'configure', tag, '-kanjifont', @kanjifont) + elsif winobj.kind_of? TkCanvas + tk_call(win, 'itemconfigure', tag, '-kanjifont', @kanjifont) + elsif winobj.kind_of? TkMenu + tk_call(win, 'entryconfigure', tag, '-kanjifont', @latinfont) + else + raise RuntimeError, "unknown widget type" + end + else +# tk_tcl2ruby(w).font_configure('kanjifont'=>@kanjifont) + tk_call(w, 'configure', '-kanjifont', @kanjifont) + end + rescue + Tk_FontUseTBL[w] = nil + end + end + } + self + end + + def latin_replace_core_tk8x(ltn) + begin + tk_call('font', 'delete', @latinfont) + rescue + end + create_latinfont(ltn) + self + end + + def kanji_replace_core_tk80(knj) + return self unless JAPANIZED_TK + + begin + tk_call('font', 'delete', @kanjifont) + rescue + end + create_kanjifont(knj) + self + end + + def kanji_replace_core_tk81(knj) + if font.kind_of? Hash + tk_call('font', 'configure', @compoundfont, *hash_kv(font)) + else + keys = {} + if font.kind_of? Array + actual_core(array2tk_list(font)).each{|key,val| keys[key] = val} + elsif font.kind_of? TkFont + actual_core(font.latin_font).each{|key,val| keys[key] = val} + else + actual_core(font).each{|key,val| keys[key] = val} + end + tk_call('font', 'configure', @compoundfont, *hash_kv(keys)) + end + self + end + + def measure_core_tk4x(window, text) + 0 + end + + def measure_core_tk8x(window, text) + if window + number(tk_call('font', 'measure', @compoundfont, + '-displayof', window, text)) + else + number(tk_call('font', 'measure', @compoundfont, text)) + end + end + + def metrics_core_tk4x(font, window, option=nil) + # dummy + if option + "" + else + Array([ ['ascent',[]], ['descent',[]], ['linespace',[]], ['fixed',[]] ]) + end + end + + def metrics_core_tk8x(font, window, option=nil) + if option + if window + number(tk_call('font', 'metrics', font, "-#{option}")) + else + number(tk_call('font', 'metrics', font, + "-displayof", window, "-#{option}")) + end + else + l = tk_split_list(if window + tk_call('font','metrics',font,"-displayof",window) + else + tk_call('font','metrics',font) + end) + r = [] + while key=l.shift + r.push [key[1..-1], l.shift.to_i] + end + r + end + end + + ################################### + # private alias + ################################### + case (Tk::TK_VERSION) + when /^4\.*/ + alias create_latinfont create_latinfont_tk4x + alias create_kanjifont create_kanjifont_tk4x + alias create_compoundfont create_compoundfont_tk4x + alias actual_core actual_core_tk4x + alias configure_core configure_core_tk4x + alias configinfo_core configinfo_core_tk4x + alias delete_core delete_core_tk4x + alias latin_replace_core latin_replace_core_tk4x + alias kanji_replace_core kanji_replace_core_tk4x + alias measure_core measure_core_tk4x + alias metrics_core metrics_core_tk4x + + when /^8\.0/ + alias create_latinfont create_latinfont_tk8x + alias create_kanjifont create_kanjifont_tk80 + alias create_compoundfont create_compoundfont_tk80 + alias actual_core actual_core_tk8x + alias configure_core configure_core_tk8x + alias configinfo_core configinfo_core_tk8x + alias delete_core delete_core_tk8x + alias latin_replace_core latin_replace_core_tk8x + alias kanji_replace_core kanji_replace_core_tk80 + alias measure_core measure_core_tk8x + alias metrics_core metrics_core_tk8x + + when /^8\.1/ + alias create_latinfont create_latinfont_tk8x + alias create_kanjifont create_kanjifont_tk81 + alias create_compoundfont create_compoundfont_tk81 + alias actual_core actual_core_tk8x + alias configure_core configure_core_tk8x + alias configinfo_core configinfo_core_tk8x + alias delete_core delete_core_tk8x + alias latin_replace_core latin_replace_core_tk8x + alias kanji_replace_core kanji_replace_core_tk81 + alias measure_core measure_core_tk8x + alias metrics_core metrics_core_tk8x + + end + + ################################### + public + ################################### + def call_font_configure(path, *args) + args += hash_kv(args.pop.update(@fontslot)) + tk_call *args + Tk_FontUseTBL[path] = self + self + end + + def used + ret = [] + Tk_FontUseTBL.each{|key,value| + if key.include?(';') + win, tag = key.split(';') + winobj = tk_tcl2ruby(win) + if winobj.kind_of? TkText + ret.push([winobj, winobj.tagid2obj(tag)]) + elsif winobj.kind_of? TkCanvas + if (tagobj = TkcTag.id2obj(tag)).kind_of? TkcTag + ret.push([winobj, tagobj]) + elsif (tagobj = TkcItem.id2obj(tag)).kind_of? TkcItem + ret.push([winobj, tagobj]) + else + ret.push([winobj, tag]) + end + elsif winobj.kind_of? TkMenu + ret.push([winobj, tag]) + else + ret.push([win, tag]) + end + else + ret.push(tk_tcl2ruby(key)) if value == self + end + } + ret + end + + def id + @id + end + + def to_eval + font + end + + def font + @compoundfont + end + + def latin_font + @latinfont + end + + def kanji_font + @kanjifont + end + + def actual(option=nil) + actual_core(@compoundfont, nil, option) + end + + def actual_displayof(window, option=nil) + window = '.' unless window + actual_core(@compoundfont, window, option) + end + + def latin_actual(option=nil) + actual_core(@latinfont, nil, option) + end + + def latin_actual_displayof(window, option=nil) + window = '.' unless window + actual_core(@latinfont, window, option) + end + + def kanji_actual(option=nil) + #if JAPANIZED_TK + if @kanjifont != "" + actual_core(@kanjifont, nil, option) + else + actual_core_tk4x(nil, nil, option) + end + end + + def kanji_actual_displayof(window, option=nil) + #if JAPANIZED_TK + if @kanjifont != "" + window = '.' unless window + actual_core(@kanjifont, window, option) + else + actual_core_tk4x(nil, window, option) + end + end + + def [](slot) + configinfo slot + end + + def []=(slot, val) + configure slot, val + end + + def configure(slot, value=None) + configure_core(@compoundfont, slot, value) + end + + def configinfo(slot=nil) + configinfo_core(@compoundfont, slot) + end + + def delete + delete_core + end + + def latin_configure(slot, value=None) + if JAPANIZED_TK + configure_core(@latinfont, slot, value) + else + configure(slot, value) + end + end + + def latin_configinfo(slot=nil) + if JAPANIZED_TK + configinfo_core(@latinfont, slot) + else + configure(slot, value) + end + end + + def kanji_configure(slot, value=None) + #if JAPANIZED_TK + if @kanjifont != "" + configure_core(@kanjifont, slot, value) + else + #"" + configure(slot, value) + end + end + + def kanji_configinfo(slot=nil) + #if JAPANIZED_TK + if @kanjifont != "" + configinfo_core(@kanjifont, slot) + else + #[] + configinfo(slot) + end + end + + def replace(ltn, knj) + latin_replace(ltn) + kanji_replace(knj) + self + end + + def latin_replace(ltn) + latin_replace_core(ltn) + end + + def kanji_replace(knj) + kanji_replace_core(knj) + end + + def measure(text) + measure_core(nil, text) + end + + def measure_displayof(window, text) + window = '.' unless window + measure_core(window, text) + end + + def metrics(option=nil) + metrics_core(@compoundfont, nil, option) + end + + def metrics_displayof(window, option=nil) + window = '.' unless window + metrics_core(@compoundfont, window, option) + end + + def latin_metrics(option=nil) + metrics_core(@latinfont, nil, option) + end + + def latin_metrics_displayof(window, option=nil) + window = '.' unless window + metrics_core(@latinfont, window, option) + end + + def kanji_metrics(option=nil) + if JAPANIZED_TK + metrics_core(@kanjifont, nil, option) + else + metrics_core_tk4x(nil, nil, option) + end + end + + def kanji_metrics_displayof(window, option=nil) + if JAPANIZED_TK + window = '.' unless window + metrics_core(@kanjifont, window, option) + else + metrics_core_tk4x(nil, window, option) + end + end + + ################################### + # public alias + ################################### + alias ascii_font latin_font + alias create_asciifont create_latinfont + alias ascii_actual latin_actual + alias ascii_actual_displayof latin_actual_displayof + alias ascii_configure latin_configure + alias ascii_configinfo latin_configinfo + alias ascii_replace latin_replace + alias ascii_metrics latin_metrics + +end + +module TkTreatTagFont + def font_configinfo + @parent.tagfont_configinfo(@id) + end + alias font font_configinfo + + def font_configure(slot) + @parent.tagfont_configure(@id, slot) + end + + def latinfont_configure(ltn, keys=nil) + @parent.latintagfont_configure(@id, ltn, keys) + end + alias asciifont_configure latinfont_configure + + def kanjifont_configure(knj, keys=nil) + @parent.kanjitagfont_configure(@id, ltn, keys) + end + + def font_copy(window, wintag=nil) + @parent.tagfont_copy(@id, window, wintag) + end + + def latinfont_copy(window, wintag=nil) + @parent.latintagfont_copy(@id, window, wintag) + end + alias asciifont_copy latinfont_copy + + def kanjifont_copy(window, wintag=nil) + @parent.kanjitagfont_copy(@id, window, wintag) + end +end diff --git a/ext/tk/lib/tkmenubar.rb b/ext/tk/lib/tkmenubar.rb new file mode 100644 index 0000000000..441f3f5c03 --- /dev/null +++ b/ext/tk/lib/tkmenubar.rb @@ -0,0 +1,137 @@ +# +# tkmenubar.rb +# +# Copyright (C) 1998 maeda shugo. All rights reserved. +# This file can be distributed under the terms of the Ruby. + +# Usage: +# +# menu_spec = [ +# [['File', 0], +# ['Open', proc{puts('Open clicked')}, 0], +# '---', +# ['Quit', proc{exit}, 0]], +# [['Edit', 0], +# ['Cut', proc{puts('Cut clicked')}, 2], +# ['Copy', proc{puts('Copy clicked')}, 0], +# ['Paste', proc{puts('Paste clicked')}, 0]] +# ] +# menubar = TkMenubar.new(nil, menu_spec, +# 'tearoff'=>false, +# 'foreground'=>'grey40', +# 'activeforeground'=>'red', +# 'font'=>'-adobe-helvetica-bold-r-*--12-*-iso8859-1') +# menubar.pack('side'=>'top', 'fill'=>'x') +# +# +# OR +# +# +# menubar = TkMenubar.new +# menubar.add_menu([['File', 0], +# ['Open', proc{puts('Open clicked')}, 0], +# '---', +# ['Quit', proc{exit}, 0]]) +# menubar.add_menu([['Edit', 0], +# ['Cut', proc{puts('Cut clicked')}, 2], +# ['Copy', proc{puts('Copy clicked')}, 0], +# ['Paste', proc{puts('Paste clicked')}, 0]]) +# menubar.configure('tearoff', false) +# menubar.configure('foreground', 'grey40') +# menubar.configure('activeforeground', 'red') +# menubar.configure('font', '-adobe-helvetica-bold-r-*--12-*-iso8859-1') +# menubar.pack('side'=>'top', 'fill'=>'x') + +# The format of the menu_spec is: +# [ +# [ +# [button text, underline, accelerator], +# [menu label, command, underline, accelerator], +# '---', # separator +# ... +# ], +# ... +# ] + +# underline and accelerator are optional parameters. +# Hashes are OK instead of Arrays. + +# To use add_menu, configuration must be done by calling configure after +# adding all menus by add_menu, not by the constructor arguments. + +require "tk" + +class TkMenubar<TkFrame + + include TkComposite + + def initialize(parent = nil, spec = nil, options = nil) + super(parent, options) + + @menus = [] + + if spec + for menu_info in spec + add_menu(menu_info) + end + end + + if options + for key, value in options + configure(key, value) + end + end + end + + def add_menu(menu_info) + btn_info = menu_info.shift + mbtn = TkMenubutton.new(@frame) + + if btn_info.kind_of?(Hash) + for key, value in btn_info + mbtn.configure(key, value) + end + elsif btn_info.kind_of?(Array) + mbtn.configure('text', btn_info[0]) if btn_info[0] + mbtn.configure('underline', btn_info[1]) if btn_info[1] + mbtn.configure('accelerator', btn_info[2]) if btn_info[2] + else + mbtn.configure('text', btn_info) + end + + menu = TkMenu.new(mbtn) + + for item_info in menu_info + if item_info.kind_of?(Hash) + menu.add('command', item_info) + elsif item_info.kind_of?(Array) + options = {} + options['label'] = item_info[0] if item_info[0] + options['command'] = item_info[1] if item_info[1] + options['underline'] = item_info[2] if item_info[2] + options['accelerator'] = item_info[3] if item_info[3] + menu.add('command', options) + elsif /^-+$/ =~ item_info + menu.add('sep') + else + menu.add('command', 'label' => item_info) + end + end + + mbtn.menu(menu) + @menus.push([mbtn, menu]) + delegate('tearoff', menu) + delegate('foreground', mbtn, menu) + delegate('background', mbtn, menu) + delegate('disabledforeground', mbtn, menu) + delegate('activeforeground', mbtn, menu) + delegate('activebackground', mbtn, menu) + delegate('font', mbtn, menu) + delegate('kanjifont', mbtn, menu) + mbtn.pack('side' => 'left') + end + + def [](index) + return @menus[index] + end +end diff --git a/ext/tk/lib/tkmngfocus.rb b/ext/tk/lib/tkmngfocus.rb new file mode 100644 index 0000000000..921fb646e7 --- /dev/null +++ b/ext/tk/lib/tkmngfocus.rb @@ -0,0 +1,27 @@ +# +# tkmngfocus.rb : methods for Tcl/Tk standard library 'focus.tcl' +# 1998/07/16 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp> +# +require 'tk' + +module TkManageFocus + extend Tk + + def TkManageFocus.followsMouse + tk_call 'tk_focusFollowsMouse' + end + + def TkManageFocus.next(window) + tk_call 'tk_focusNext', window + end + def focusNext + TkManageFocus.next(self) + end + + def TkManageFocus.prev(window) + tk_call 'tk_focusPrev', window + end + def focusPrev + TkManageFocus.prev(self) + end +end diff --git a/ext/tk/lib/tkpalette.rb b/ext/tk/lib/tkpalette.rb new file mode 100644 index 0000000000..a2dc7c87cb --- /dev/null +++ b/ext/tk/lib/tkpalette.rb @@ -0,0 +1,48 @@ +# +# tkpalette.rb : methods for Tcl/Tk standard library 'palette.tcl' +# 1998/06/21 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp> +# +require 'tk' + +module TkPalette + include Tk + extend Tk + + def TkPalette.set(*args) + args = args.to_a.flatten if args.kind_of? Hash + tk_call 'tk_setPalette', *args + end + def TkPalette.setPalette(*args) + TkPalette.set(*args) + end + + def TkPalette.bisque + tk_call 'tk_bisque' + end + + def TkPalette.darken(color, percent) + tk_call 'tkDarken', color, percent + end + + def TkPalette.recolorTree(window, colors) + if not colors.kind_of?(Hash) + fail "2nd arg need to be Hash" + end + + colors.each{|key, value| + begin + if window.cget(key) == tk_call('set', "tkPalette(#{key})") + window[key] = colors[key] + end + rescue + # ignore + end + } + + TkWinfo.children(window).each{|w| TkPalette.recolorTree(w, colors)} + end + + def recolorTree(colors) + TkPalette.recolorTree(self, colors) + end +end diff --git a/ext/tk/lib/tkscrollbox.rb b/ext/tk/lib/tkscrollbox.rb new file mode 100644 index 0000000000..8d129b2f4b --- /dev/null +++ b/ext/tk/lib/tkscrollbox.rb @@ -0,0 +1,27 @@ +# +# tkscrollbox.rb - Tk Listbox with Scrollbar +# as an example of Composite Widget +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require 'tk.rb' + +class TkScrollbox<TkListbox + include TkComposite + def initialize_composite + list = TkListbox.new(@frame) + scroll = TkScrollbar.new(@frame) + @path = list.path + + list.configure 'yscroll', scroll.path+" set" + list.pack 'side'=>'left','fill'=>'both','expand'=>'yes' + scroll.configure 'command', list.path+" yview" + scroll.pack 'side'=>'right','fill'=>'y' + + delegate('DEFAULT', list) + delegate('foreground', list) + delegate('background', list, scroll) + delegate('borderwidth', @frame) + delegate('relief', @frame) + end +end diff --git a/ext/tk/lib/tktext.rb b/ext/tk/lib/tktext.rb new file mode 100644 index 0000000000..02d5a7f3e0 --- /dev/null +++ b/ext/tk/lib/tktext.rb @@ -0,0 +1,797 @@ +# +# tktext.rb - Tk text classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require 'tk.rb' +require 'tkfont' + +module TkTreatTextTagFont + def tagfont_configinfo(tag) + if tag.kind_of? TkTextTag + pathname = self.path + ';' + tag.id + else + pathname = self.path + ';' + tag + end + ret = TkFont.used_on(pathname) + if ret == nil + ret = TkFont.init_widget_font(pathname, + self.path, 'tag', 'configure', tag) + end + ret + end + alias tagfontobj tagfont_configinfo + + def tagfont_configure(tag, slot) + if tag.kind_of? TkTextTag + pathname = self.path + ';' + tag.id + else + pathname = self.path + ';' + tag + end + if (fnt = slot['font']) + slot['font'] = nil + if fnt.kind_of? TkFont + return fnt.call_font_configure(pathname, + self.path,'tag','configure',tag,slot) + else + latintagfont_configure(tag, fnt) if fnt + end + end + if (ltn = slot['latinfont']) + slot['latinfont'] = nil + latintagfont_configure(tag, ltn) if ltn + end + if (ltn = slot['asciifont']) + slot['asciifont'] = nil + latintagfont_configure(tag, ltn) if ltn + end + if (knj = slot['kanjifont']) + slot['kanjifont'] = nil + kanjitagfont_configure(tag, knj) if knj + end + + tk_call(self.path, 'tag', 'configure', tag, *hash_kv(slot)) if slot != {} + self + end + + def latintagfont_configure(tag, ltn, keys=nil) + fobj = tagfontobj(tag) + if ltn.kind_of? TkFont + conf = {} + ltn.latin_configinfo.each{|key,val| conf[key] = val if val != []} + if conf == {} + fobj.latin_replace(ltn) + fobj.latin_configure(keys) if keys + elsif keys + fobj.latin_configure(conf.update(keys)) + else + fobj.latin_configure(conf) + end + else + fobj.latin_replace(ltn) + end + end + alias asciitagfont_configure latintagfont_configure + + def kanjitagfont_configure(tag, knj, keys=nil) + fobj = tagfontobj(tag) + if knj.kind_of? TkFont + conf = {} + knj.kanji_configinfo.each{|key,val| conf[key] = val if val != []} + if conf == {} + fobj.kanji_replace(knj) + fobj.kanji_configure(keys) if keys + elsif keys + fobj.kanji_configure(conf.update(keys)) + else + fobj.kanji_configure(conf) + end + else + fobj.kanji_replace(knj) + end + end + + def tagfont_copy(tag, window, wintag=nil) + if wintag + window.tagfontobj(wintag).configinfo.each{|key,value| + tagfontobj(tag).configure(key,value) + } + tagfontobj(tag).replace(window.tagfontobj(wintag).latin_font, + window.tagfontobj(wintag).kanji_font) + else + window.tagfont(wintag).configinfo.each{|key,value| + tagfontobj(tag).configure(key,value) + } + tagfontobj(tag).replace(window.fontobj.latin_font, + window.fontobj.kanji_font) + end + end + + def latintagfont_copy(tag, window, wintag=nil) + if wintag + tagfontobj(tag).latin_replace(window.tagfontobj(wintag).latin_font) + else + tagfontobj(tag).latin_replace(window.fontobj.latin_font) + end + end + alias asciitagfont_copy latintagfont_copy + + def kanjitagfont_copy(tag, window, wintag=nil) + if wintag + tagfontobj(tag).kanji_replace(window.tagfontobj(wintag).kanji_font) + else + tagfontobj(tag).kanji_replace(window.fontobj.kanji_font) + end + end +end + +class TkText<TkTextWin + include TkTreatTextTagFont + + WidgetClassName = 'Text'.freeze + TkClassBind::WidgetClassNameTBL[WidgetClassName] = self + def self.to_eval + WidgetClassName + end + include Scrollable + def create_self + tk_call 'text', @path + @tags = {} + end + def index(index) + tk_send 'index', index + end + def value + tk_send 'get', "1.0", "end - 1 char" + end + def value= (val) + tk_send 'delete', "1.0", 'end' + tk_send 'insert', "1.0", val + end + def _addcmd(cmd) + @cmdtbl.push cmd + end + def _addtag(name, obj) + @tags[name] = obj + end + + def tagid2obj(tagid) + if not @tags[tagid] + tagid + else + @tags[tagid] + end + end + + def tag_names(index=None) + tk_split_list(tk_send('tag', 'names', index)).collect{|elt| + tagid2obj(elt) + } + end + def window_names + tk_send('window', 'names').collect{|elt| + tagid2obj(elt) + } + end + def image_names + tk_send('image', 'names').collect{|elt| + tagid2obj(elt) + } + end + + def set_insert(index) + tk_send 'mark', 'set', 'insert', index + end + def set_current(index) + tk_send 'mark', 'set', 'current', index + end + + def insert(index, chars, *tags) + super index, chars, tags.collect{|x|_get_eval_string(x)}.join(' ') + end + + def destroy + @tags.each_value do |t| + t.destroy + end + super + end + + def backspace + self.delete 'insert' + end + + def compare(idx1, op, idx2) + bool(tk_send('compare', idx1, op, idx2)) + end + + def debug + bool(tk_send('debug')) + end + def debug=(boolean) + tk_send 'debug', boolean + end + + def bbox(index) + inf = tk_send('bbox', index) + (inf == "")? [0,0,0,0]: inf + end + def dlineinfo(index) + inf = tk_send('dlineinfo', index) + (inf == "")? [0,0,0,0,0]: inf + end + + def yview(*what) + tk_send 'yview', *what + end + def yview_pickplace(*what) + tk_send 'yview', '-pickplace', *what + end + + def xview(*what) + tk_send 'xview', *what + end + def xview_pickplace(*what) + tk_send 'xview', '-pickplace', *what + end + + def tag_add(tag,index1,index2=None) + tk_send 'tag', 'add', tag, index1, index2 + end + + def _tag_bind_core(mode, tag, seq, cmd=Proc.new, args=nil) + id = install_bind(cmd, args) + tk_send 'tag', 'bind', tag, "<#{tk_event_sequence(seq)}>", mode + id + # _addcmd cmd + end + private :_tag_bind_core + + def tag_bind(tag, seq, cmd=Proc.new, args=nil) + _tag_bind_core('', tag, seq, cmd=Proc.new, args=nil) + end + + def tag_bind_append(tag, seq, cmd=Proc.new, args=nil) + _tag_bind_core('+', tag, seq, cmd=Proc.new, args=nil) + end + + def tag_bindinfo(tag, context=nil) + if context + (tk_send('tag', 'bind', tag, + "<#{tk_event_sequence(context)}>")).collect{|cmdline| + if cmdline =~ /^rb_out (c\d+)\s+(.*)$/ + [Tk_CMDTBL[$1], $2] + else + cmdline + end + } + else + tk_split_list(tk_send('tag', 'bind', tag)).filter{|seq| + seq[1..-2].gsub(/></,',') + } + end + end + + def tag_cget(tag, key) + tk_tcl2ruby tk_call @t.path, 'tag', 'cget', tag, "-#{key}" + end + + def tag_configure(tag, key, val=None) + if key.kind_of? Hash + if ( key['font'] || key['kanjifont'] \ + || key['latinfont'] || key['asciifont'] ) + tagfont_configure(tag, key.dup) + else + tk_send 'tag', 'configure', tag, *hash_kv(key) + end + + else + if ( key == 'font' || key == 'kanjifont' \ + || key == 'latinfont' || key == 'asciifont' ) + tagfont_configure({key=>val}) + else + tk_call 'tag', 'configure', tag, "-#{key}", val + end + end + end + + def tag_configinfo(tag, key=nil) + if key + conf = tk_split_list(tk_send('tag','configure',tag,"-#{key}")) + conf[0] = conf[0][1..-1] + conf + else + tk_split_list(tk_send('tag', 'configure', tag)).collect{|conf| + conf[0] = conf[0][1..-1] + conf + } + end + end + + def tag_raise(tag, above=None) + tk_send 'tag', 'raise', tag, above + end + + def tag_lower(tag, below=None) + tk_send 'tag', 'lower', tag, below + end + + def tag_remove(tag, *index) + tk_send 'tag', 'remove', tag, *index + end + + def tag_ranges(tag) + l = tk_split_list(tk_send('tag', 'ranges', tag)) + r = [] + while key=l.shift + r.push [key, l.shift] + end + r + end + + def tag_nextrange(tag, first, last=None) + tk_split_list(tk_send('tag', 'nextrange', tag, first, last)) + end + + def tag_prevrange(tag, first, last=None) + tk_split_list(tk_send('tag', 'prevrange', tag, first, last)) + end + + def search_with_length(pat,start,stop=None) + pat = pat.char if pat.kind_of? Integer + if stop != None + return ["", 0] if compare(start,'>=',stop) + txt = get(start,stop) + if (pos = txt.index(pat)) + pos = txt[0..(pos-1)].split('').length if pos > 0 + if pat.kind_of? String + return [index(start + " + #{pos} chars"), pat.split('').length] + else + return [index(start + " + #{pos} chars"), $&.split('').length] + end + else + return ["", 0] + end + else + txt = get(start,'end - 1 char') + if (pos = txt.index(pat)) + pos = txt[0..(pos-1)].split('').length if pos > 0 + if pat.kind_of? String + return [index(start + " + #{pos} chars"), pat.split('').length] + else + return [index(start + " + #{pos} chars"), $&.split('').length] + end + else + txt = get('1.0','end - 1 char') + if (pos = txt.index(pat)) + pos = txt[0..(pos-1)].split('').length if pos > 0 + if pat.kind_of? String + return [index("1.0 + #{pos} chars"), pat.split('').length] + else + return [index("1.0 + #{pos} chars"), $&.split('').length] + end + else + return ["", 0] + end + end + end + end + + def search(pat,start,stop=None) + search_with_length(pat,start,stop)[0] + end + + def rsearch_with_length(pat,start,stop=None) + pat = pat.char if pat.kind_of? Integer + if stop != None + return ["", 0] if compare(start,'<=',stop) + txt = get(stop,start) + if (pos = txt.rindex(pat)) + pos = txt[0..(pos-1)].split('').length if pos > 0 + if pat.kind_of? String + return [index(stop + " + #{pos} chars"), pat.split('').length] + else + return [index(stop + " + #{pos} chars"), $&.split('').length] + end + else + return ["", 0] + end + else + txt = get('1.0',start) + if (pos = txt.rindex(pat)) + pos = txt[0..(pos-1)].split('').length if pos > 0 + if pat.kind_of? String + return [index("1.0 + #{pos} chars"), pat.split('').length] + else + return [index("1.0 + #{pos} chars"), $&.split('').length] + end + else + txt = get('1.0','end - 1 char') + if (pos = txt.rindex(pat)) + pos = txt[0..(pos-1)].split('').length if pos > 0 + if pat.kind_of? String + return [index("1.0 + #{pos} chars"), pat.split('').length] + else + return [index("1.0 + #{pos} chars"), $&.split('').length] + end + else + return ["", 0] + end + end + end + end + + def rsearch(pat,start,stop=None) + rsearch_with_length(pat,start,stop)[0] + end +end + +class TkTextTag<TkObject + include TkTreatTagFont + + $tk_text_tag = 'tag0000' + def initialize(parent, keys=nil) + if not parent.kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @parent = @t = parent + @path = @id = $tk_text_tag + $tk_text_tag = $tk_text_tag.succ + #tk_call @t.path, "tag", "configure", @id, *hash_kv(keys) + configure(keys) if keys + @t._addtag id, self + end + def id + return @id + end + + def first + @id + '.first' + end + + def last + @id + '.last' + end + + def add(*index) + tk_call @t.path, 'tag', 'add', @id, *index + end + + def remove(*index) + tk_call @t.path, 'tag', 'remove', @id, *index + end + + def ranges + l = tk_split_list(tk_call(@t.path, 'tag', 'ranges', @id)) + r = [] + while key=l.shift + r.push [key, l.shift] + end + r + end + + def nextrange(first, last=None) + tk_split_list(tk_call(@t.path, 'tag', 'nextrange', @id, first, last)) + end + + def prevrange(first, last=None) + tk_split_list(tk_call(@t.path, 'tag', 'prevrange', @id, first, last)) + end + + def [](key) + cget key + end + + def []=(key,val) + configure key, val + end + + def cget(key) + tk_tcl2ruby tk_call @t.path, 'tag', 'cget', @id, "-#{key}" + end + + def configure(key, val=None) + @t.tag_configure @id, key, val + end +# def configure(key, val=None) +# if key.kind_of? Hash +# tk_call @t.path, 'tag', 'configure', @id, *hash_kv(key) +# else +# tk_call @t.path, 'tag', 'configure', @id, "-#{key}", val +# end +# end +# def configure(key, value) +# if value == FALSE +# value = "0" +# elsif value.kind_of? Proc +# value = install_cmd(value) +# end +# tk_call @t.path, 'tag', 'configure', @id, "-#{key}", value +# end + + def configinfo(key=nil) + @t.tag_configinfo @id, key + end +# def configinfo(key=nil) +# if key +# conf = tk_split_list(tk_call(@t.path, 'tag','configure',@id,"-#{key}")) +# conf[0] = conf[0][1..-1] +# conf +# else +# tk_split_list(tk_call(@t.path, 'tag', 'configure', @id)).collect{|conf| +# conf[0] = conf[0][1..-1] +# conf +# } +# end +# end + + def bind(seq, cmd=Proc.new, args=nil) + id = install_bind(cmd, args) + tk_call @t.path, 'tag', 'bind', @id, "<#{tk_event_sequence(seq)}>", id + # @t._addcmd cmd + end + + def bindinfo(context=nil) + if context + (tk_call(@t.path, 'tag', 'bind', @id, + "<#{tk_event_sequence(context)}>")).collect{|cmdline| + if cmdline =~ /^rb_out (c\d+)\s+(.*)$/ + [Tk_CMDTBL[$1], $2] + else + cmdline + end + } + else + tk_split_list(tk_call(@t.path, 'tag', 'bind', @id)).filter{|seq| + seq[1..-2].gsub(/></,',') + } + end + end + + def raise(above=None) + tk_call @t.path, 'tag', 'raise', @id, above + end + + def lower(below=None) + tk_call @t.path, 'tag', 'lower', @id, below + end + + def destroy + tk_call @t.path, 'tag', 'delete', @id + end +end + +class TkTextTagSel<TkTextTag + def initialize(parent, keys=nil) + if not parent.kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + @path = @id = 'sel' + #tk_call @t.path, "tag", "configure", @id, *hash_kv(keys) + configure(keys) if keys + @t._addtag id, self + end +end + +class TkTextMark<TkObject + $tk_text_mark = 'mark0000' + def initialize(parent, index) + if not parent.kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + @path = @id = $tk_text_mark + $tk_text_mark = $tk_text_mark.succ + tk_call @t.path, 'mark', 'set', @id, index + @t._addtag id, self + end + def id + return @id + end + + def set(where) + tk_call @t.path, 'mark', 'set', @id, where + end + + def unset + tk_call @t.path, 'mark', 'unset', @id + end + alias destroy unset + + def gravity + tk_call @t.path, 'mark', 'gravity', @id + end + + def gravity=(direction) + tk_call @t.path, 'mark', 'gravity', @id, direction + end +end + +class TkTextMarkInsert<TkTextMark + def initialize(parent, index=nil) + if not parent.kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + @path = @id = 'insert' + tk_call @t.path, 'mark', 'set', @id, index if index + @t._addtag id, self + end +end + +class TkTextMarkCurrent<TkTextMark + def initialize(parent,index=nil) + if not parent.kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + @path = @id = 'current' + tk_call @t.path, 'mark', 'set', @id, index if index + @t._addtag id, self + end +end + +class TkTextWindow<TkObject + def initialize(parent, index, keys) + if not parent.kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + if index == 'end' + @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars')) + elsif index.kind_of? TkTextMark + if tk_call(@t.path,'index',index.path) == tk_call(@t.path,'index','end') + @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars')) + else + @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index.path)) + end + else + @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index)) + end + @path.gravity = 'left' + @index = @path.path + @id = keys['window'] + if keys['create'] + @p_create = keys['create'] + if @p_create.kind_of? Proc + keys['create'] = install_cmd(proc{@id = @p_create.call; @id.path}) + end + end + tk_call @t.path, 'window', 'create', @index, *hash_kv(keys) + end + + def [](slot) + cget(slot) + end + def []=(slot, value) + configure(slot, value) + end + + def cget(slot) + tk_tcl2ruby tk_call @t.path, 'window', 'cget', @index, "-#{slot}" + end + + def configure(slot, value=None) + if slot.kind_of? Hash + @id = slot['window'] if slot['window'] + if slot['create'] + self.create=value + slot['create']=nil + end + if slot.size > 0 + tk_call @t.path, 'window', 'configure', @index, *hash_kv(slot) + end + else + @id = value if slot == 'window' + if slot == 'create' + self.create=value + else + tk_call @t.path, 'window', 'configure', @index, "-#{slot}", value + end + end + end + + def window + @id + end + + def window=(value) + tk_call @t.path, 'window', 'configure', @index, '-window', value + @id = value + end + + def create + @p_create + end + + def create=(value) + @p_create = value + if @p_create.kind_of? Proc + value = install_cmd(proc{@id = @p_create.call}) + end + tk_call @t.path, 'window', 'configure', @index, '-create', value + end + + def configinfo(slot = nil) + if slot + conf = tk_split_list(tk_call @t.path, 'window', 'configure', + @index, "-#{slot}") + conf[0] = conf[0][1..-1] + conf + else + tk_split_list(tk_call @t.path, 'window', 'configure', + @index).collect{|conf| + conf[0] = conf[0][1..-1] + conf + } + end + end +end + +class TkTextImage<TkObject + def initialize(parent, index, keys) + if not parent.kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + if index == 'end' + @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars')) + elsif index.kind_of? TkTextMark + if tk_call(@t.path,'index',index.path) == tk_call(@t.path,'index','end') + @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars')) + else + @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index.path)) + end + else + @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index)) + end + @path.gravity = 'left' + @index = @path.path + @id = tk_call(@t.path, 'image', 'create', @index, *hash_kv(keys)) + end + + def [](slot) + cget(slot) + end + def []=(slot, value) + configure(slot, value) + end + + def cget(slot) + tk_tcl2ruby tk_call @t.path, 'image', 'cget', @index, "-#{slot}" + end + + def configure(slot, value=None) + if slot.kind_of? Hash + tk_call @t.path, 'image', 'configure', @index, *hash_kv(slot) + else + tk_call @t.path, 'image', 'configure', @index, "-#{slot}", value + end + end +# def configure(slot, value) +# tk_call @t.path, 'image', 'configure', @index, "-#{slot}", value +# end + + def image + tk_call @t.path, 'image', 'configure', @index, '-image' + end + + def image=(value) + tk_call @t.path, 'image', 'configure', @index, '-image', value + end + + def configinfo(slot = nil) + if slot + conf = tk_split_list(tk_call @t.path, 'image', 'configure', + @index, "-#{slot}") + conf[0] = conf[0][1..-1] + conf + else + tk_split_list(tk_call @t.path, 'image', 'configure', + @index).collect{|conf| + conf[0] = conf[0][1..-1] + conf + } + end + end +end diff --git a/ext/tk/lib/tkvirtevent.rb b/ext/tk/lib/tkvirtevent.rb new file mode 100644 index 0000000000..0d100c2186 --- /dev/null +++ b/ext/tk/lib/tkvirtevent.rb @@ -0,0 +1,66 @@ +# +# tkvirtevent.rb : treats virtual events +# 1998/07/16 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp> +# +require 'tk' + +class TkVirtualEvent<TkObject + extend Tk + + TkVirturlEventID = [0] + TkVirturlEventTBL = {} + + def TkVirtualEvent.getobj(event) + obj = TkVirturlEventTBL[event] + obj ? obj : event + end + + def TkVirtualEvent.info + tk_call('event', 'info').split(/\s+/).filter{|seq| + TkVirtualEvent.getobj(seq[1..-2]) + } + end + + def initialize(*sequences) + @path = @id = format("<VirtEvent%.4d>", TkVirturlEventID[0]) + TkVirturlEventID[0] += 1 + add(*sequences) + end + + def add(*sequences) + if sequences != [] + tk_call('event', 'add', "<#{@id}>", + *(sequences.collect{|seq| "<#{tk_event_sequence(seq)}>"}) ) + TkVirturlEventTBL[@id] = self + end + self + end + + def delete(*sequences) + if sequences == [] + tk_call('event', 'delete', "<#{@id}>") + TkVirturlEventTBL[@id] = nil + else + tk_call('event', 'delete', "<#{@id}>", + *(sequences.collect{|seq| "<#{tk_event_sequence(seq)}>"}) ) + TkVirturlEventTBL[@id] = nil if info == [] + end + self + end + + def info + tk_call('event', 'info', "<#{@id}>").split(/\s+/).filter{|seq| + l = seq.scan(/<*[^<>]+>*/).filter{|subseq| + case (subseq) + when /^<<[^<>]+>>$/ + TkVirtualEvent.getobj(subseq[1..-2]) + when /^<[^<>]+>$/ + subseq[1..-2] + else + subseq.split('') + end + }.flatten + (l.size == 1) ? l[0] : l + } + end +end diff --git a/ext/tk/sample/tkbiff.rb b/ext/tk/sample/tkbiff.rb new file mode 100644 index 0000000000..d2d7bf7beb --- /dev/null +++ b/ext/tk/sample/tkbiff.rb @@ -0,0 +1,149 @@ +#! /usr/local/bin/ruby + +if ARGV[0] != '-d' + unless $DEBUG + exit if fork + end +else + ARGV.shift +end + +if ARGV.length == 0 + if ENV['MAIL'] + $spool = ENV['MAIL'] + else + $spool = '/usr/spool/mail/' + ENV['USER'] + end +else + $spool = ARGV[0] +end + +require "parsedate" +require "base64" + +include ParseDate + +class Mail + def Mail.new(f) + if !f.kind_of?(IO) + f = open(f, "r") + me = super + f.close + else + me = super + end + return me + end + + def initialize(f) + @header = {} + @body = [] + while f.gets() + $_.chop! + next if /^From / # skip From-line + break if /^$/ # end of header + if /^(\S+):\s*(.*)/ + @header[attr = $1.capitalize] = $2 + elsif attr + sub(/^\s*/, '') + @header[attr] += "\n" + $_ + end + end + + return if ! $_ + + while f.gets() + break if /^From / + @body.push($_) + end + end + + def header + return @header + end + + def body + return @body + end + +end + +require "tkscrollbox" + +$top = TkRoot.new +$top.withdraw +$list = TkScrollbox.new($top) { + relief 'raised' + width 80 + height 8 + setgrid 'yes' + pack +} +TkButton.new($top) { + text 'Dismiss' + command proc {$top.withdraw} + pack('fill'=>'both','expand'=>'yes') +} +$top.bind "Control-c", proc{exit} +$top.bind "Control-q", proc{exit} +$top.bind "space", proc{exit} + +$spool_size = 0 +$check_time = Time.now + +def check + $check_time = Time.now + size = File.size($spool) + if size and size != $spool_size + $spool_size = size + pop_up if size > 0 + end + Tk.after 5000, proc{check} +end + +if defined? Thread + Thread.start do + loop do + sleep 600 + if Time.now - $check_time > 200 + Tk.after 5000, proc{check} + end + end + end +end + +def pop_up + outcount = 0; + $list.delete 0, 'end' + f = open($spool, "r") + while !f.eof? + mail = Mail.new(f) + date, from, subj = mail.header['Date'], mail.header['From'], mail.header['Subject'] + next if !date + y = m = d = 0 + y, m, d = parsedate(date) if date + from = "sombody@somewhere" if ! from + subj = "(nil)" if ! subj + from = decode_b(from) + subj = decode_b(subj) + $list.insert 'end', format('%-02d/%02d/%02d [%-28.28s] %s',y,m,d,from,subj) + outcount += 1 + end + f.close + if outcount == 0 + $list.insert 'end', "You have no mail." + else + $list.see 'end' + end + $top.deiconify + Tk.after 2000, proc{$top.withdraw} +end + +$list.insert 'end', "You have no mail." +check +Tk.after 2000, proc{$top.withdraw} +begin + Tk.mainloop +rescue + `echo #$! > /tmp/tkbiff` +end diff --git a/ext/tk/sample/tkbrowse.rb b/ext/tk/sample/tkbrowse.rb new file mode 100644 index 0000000000..882f0a489b --- /dev/null +++ b/ext/tk/sample/tkbrowse.rb @@ -0,0 +1,79 @@ +#!/usr/local/bin/ruby +# +# This script generates a directory browser, which lists the working +# directory and allows you to open files or subdirectories by +# double-clicking. + +# Create a scrollbar on the right side of the main window and a listbox +# on the left side. + +require "tkscrollbox" + +# The procedure below is invoked to open a browser on a given file; if the +# file is a directory then another instance of this program is invoked; if +# the file is a regular file then the Mx editor is invoked to display +# the file. + +$dirlist = {} + +def browsedir (dir) + if $dirlist.key? dir + $dirlist[dir] + else + top = if $dirlist.size > 0 then TkToplevel.new else nil end + list = TkScrollbox.new(top) { + relief 'raised' + width 20 + height 20 + setgrid 'yes' + pack + } + list.insert 'end', *`ls #{dir}`.split + + # Set up bindings for the browser. + + list.focus + list.bind "Control-q", proc{exit} + list.bind "Control-c", proc{exit} + list.bind "Control-p", proc{ + print "selection <", TkSelection.get, ">\n" + } + + list.bind "Double-Button-1", proc{ + for i in TkSelection.get.split + print "clicked ", i, "\n" + browse dir, i + end + } + $dirlist[dir] = list + end +end + +def browse (dir, file) + file="#{dir}/#{file}" + if File.directory? file + browsedir(file) + else + if File.file? file + if ENV['EDITOR'] + system format("%s %s&", ENV['EDITOR'], file) + else + system "xedit #{file}&" + end + else + STDERR.print "\"#{file}\" isn't a directory or regular file" + end + end +end + +# Fill the listbox with a list of all the files in the directory (run +# the "ls" command to get that information). + +if ARGV.length>0 + dir = ARGV[0] +else + dir="." +end + +browsedir(dir) +Tk.mainloop diff --git a/ext/tk/sample/tkdialog.rb b/ext/tk/sample/tkdialog.rb new file mode 100644 index 0000000000..e83e16d0a8 --- /dev/null +++ b/ext/tk/sample/tkdialog.rb @@ -0,0 +1,62 @@ +#! /usr/local/bin/ruby +require "tk" + +root = TkFrame.new +top = TkFrame.new(root) { + relief 'raised' + border 1 +} +msg = TkMessage.new(top) { + text "File main.c hasn't been saved to disk since \ +it was last modified. What should I do?" + justify 'center' + aspect 200 + font '-Adobe-helvetica-medium-r-normal--*-240*' + pack('padx'=>5, 'pady'=>5, 'expand'=>'yes') +} +top.pack('fill'=>'both') +root.pack + +bot = TkFrame.new(root) { + relief 'raised' + border 1 +} + +TkFrame.new(bot) { |left| + relief 'sunken' + border 1 + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10, 'pady'=> 10) + TkButton.new(left) { + text "Save File" + command "quit 'save'" + pack('expand'=>'yes','padx'=>6,'pady'=> 6) + top.bind "Enter", proc{state 'active'} + msg.bind "Enter", proc{state 'active'} + bot.bind "Enter", proc{state 'active'} + top.bind "Leave", proc{state 'normal'} + msg.bind "Leave", proc{state 'normal'} + bot.bind "Leave", proc{state 'normal'} + Tk.root.bind "ButtonRelease-1", proc{quit 'save'} + Tk.root.bind "Return", proc{quit 'save'} + } +} +TkButton.new(bot) { + text "Quit Anyway" + command "quit 'quit'" + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10) +} +TkButton.new(bot) { + text "Return To Editor" + command "quit 'return'" + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10) +} +bot.pack +root.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes') + +def quit(button) + print "aaa\n" + print "You pressed the \"#{button}\" button; bye-bye!\n" + exit +end + +Tk.mainloop diff --git a/ext/tk/sample/tkfrom.rb b/ext/tk/sample/tkfrom.rb new file mode 100644 index 0000000000..ba0e547799 --- /dev/null +++ b/ext/tk/sample/tkfrom.rb @@ -0,0 +1,132 @@ +#! /usr/local/bin/ruby + +require "parsedate" +require "base64" + +include ParseDate + +class Mail + def Mail.new(f) + if !f.kind_of?(IO) + f = open(f, "r") + me = super(f) + f.close + else + me = super + end + return me + end + + def initialize(f) + @header = {} + @body = [] + while f.gets() + $_.chop! + next if /^From / # skip From-line + break if /^$/ # end of header + if /^(\S+):\s*(.*)/ + @header[attr = $1.capitalize] = $2 + elsif attr + sub(/^\s*/, '') + @header[attr] += "\n" + $_ + end + end + + return if ! $_ + + while f.gets() + break if /^From / + @body.push($_) + end + end + + def header + return @header + end + + def body + return @body + end + +end + +if ARGV.length == 0 + if ENV['MAIL'] + ARGV[0] = ENV['MAIL'] + elsif ENV['USER'] + ARGV[0] = '/usr/spool/mail/' + ENV['USER'] + elsif ENV['LOGNAME'] + ARGV[0] = '/usr/spool/mail/' + ENV['LOGNAME'] + end +end + +require "tk" +list = scroll = nil +TkFrame.new{|f| + list = TkListbox.new(f) { + yscroll proc{|idx| + scroll.set *idx + } + relief 'raised' +# geometry "80x5" + width 80 + height 5 + setgrid 'yes' + pack('side'=>'left','fill'=>'both','expand'=>'yes') + } + scroll = TkScrollbar.new(f) { + command proc{|idx| + list.yview *idx + } + pack('side'=>'right','fill'=>'y') + } + pack +} +root = Tk.root +TkButton.new(root) { + text 'Dismiss' + command proc {exit} + pack('fill'=>'both','expand'=>'yes') +} +root.bind "Control-c", proc{exit} +root.bind "Control-q", proc{exit} +root.bind "space", proc{exit} + +$outcount = 0; +for file in ARGV + next if File.exist?(file) + atime = File.atime(file) + mtime = File.mtime(file) + f = open(file, "r") + begin + until f.eof + mail = Mail.new(f) + date = mail.header['Date'] + next unless date + from = mail.header['From'] + subj = mail.header['Subject'] + y = m = d = 0 + y, m, d = parsedate(date) if date + from = "sombody@somewhere" unless from + subj = "(nil)" unless subj + from = decode_b(from) + subj = decode_b(subj) + list.insert 'end', format('%-02d/%02d/%02d [%-28.28s] %s',y,m,d,from,subj) + $outcount += 1 + end + ensure + f.close + File.utime(atime, mtime, file) + list.see 'end' + end +end + +limit = 10000 +if $outcount == 0 + list.insert 'end', "You have no mail." + limit = 2000 +end +Tk.after limit, proc{ + exit +} +Tk.mainloop diff --git a/ext/tk/sample/tkhello.rb b/ext/tk/sample/tkhello.rb new file mode 100644 index 0000000000..5188fe1c8c --- /dev/null +++ b/ext/tk/sample/tkhello.rb @@ -0,0 +1,10 @@ +require "tk" + +TkButton.new(nil, + 'text' => 'hello', + 'command' => proc{print "hello\n"}).pack('fill'=>'x') +TkButton.new(nil, + 'text' => 'quit', + 'command' => 'exit').pack('fill'=>'x') + +Tk.mainloop diff --git a/ext/tk/sample/tkline.rb b/ext/tk/sample/tkline.rb new file mode 100644 index 0000000000..2406b0749f --- /dev/null +++ b/ext/tk/sample/tkline.rb @@ -0,0 +1,45 @@ + +require "tkclass" + +$tkline_init = FALSE +def start_random + return if $tkline_init + $tkline_init = TRUE + if defined? Thread + Thread.start do + loop do + sleep 2 + Line.new($c, rand(400), rand(200), rand(400), rand(200)) + end + end + end +end + +$c = Canvas.new +$c.pack +$start_x = start_y = 0 + +def do_press(x, y) + $start_x = x + $start_y = y + $current_line = Line.new($c, x, y, x, y) + start_random +end +def do_motion(x, y) + if $current_line + $current_line.coords $start_x, $start_y, x, y + end +end + +def do_release(x, y) + if $current_line + $current_line.coords $start_x, $start_y, x, y + $current_line.fill 'black' + $current_line = nil + end +end + +$c.bind("1", proc{|e| do_press e.x, e.y}) +$c.bind("B1-Motion", proc{|x, y| do_motion x, y}, "%x %y") +$c.bind("ButtonRelease-1", proc{|x, y| do_release x, y}, "%x %y") +Tk.mainloop diff --git a/ext/tk/sample/tktimer.rb b/ext/tk/sample/tktimer.rb new file mode 100644 index 0000000000..34377e2f39 --- /dev/null +++ b/ext/tk/sample/tktimer.rb @@ -0,0 +1,50 @@ +#!/usr/local/bin/ruby +# This script generates a counter with start and stop buttons. + +require "tk" +$label = TkLabel.new { + text '0.00' + relief 'raised' + width 10 + pack('side'=>'bottom', 'fill'=>'both') +} + +TkButton.new { + text 'Start' + command proc { + if $stopped + $stopped = FALSE + tick + end + } + pack('side'=>'left','fill'=>'both','expand'=>'yes') +} +TkButton.new { + text 'Stop' + command proc{ + exit if $stopped + $stopped = TRUE + } + pack('side'=>'right','fill'=>'both','expand'=>'yes') +} + +$seconds=0 +$hundredths=0 +$stopped=TRUE + +def tick + if $stopped then return end + Tk.after 50, proc{tick} + $hundredths+=5 + if $hundredths >= 100 + $hundredths=0 + $seconds+=1 + end + $label.text format("%d.%02d", $seconds, $hundredths) +end + +root = Tk.root +root.bind "Control-c", proc{root.destroy} +root.bind "Control-q", proc{root.destroy} +Tk.root.focus +Tk.mainloop diff --git a/ext/tk/tkutil.c b/ext/tk/tkutil.c new file mode 100644 index 0000000000..e93733bb67 --- /dev/null +++ b/ext/tk/tkutil.c @@ -0,0 +1,45 @@ +/************************************************ + + tk.c - + + $Author$ + $Date$ + created at: Fri Nov 3 00:47:54 JST 1995 + +************************************************/ + +#include "ruby.h" + +static VALUE +tk_eval_cmd(argc, argv) + int argc; + VALUE argv[]; +{ + VALUE cmd, rest; + + rb_scan_args(argc, argv, "1*", &cmd, &rest); + return rb_eval_cmd(cmd, rest); +} + +static VALUE +tk_s_new(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE obj = rb_obj_alloc(class); + + rb_funcall2(obj, rb_intern("initialize"), argc, argv); + if (rb_iterator_p()) rb_obj_instance_eval(0, 0, obj); + return obj; +} + +Init_tkutil() +{ + VALUE mTK = rb_define_module("TkUtil"); + VALUE cTK = rb_define_class("TkKernel", rb_cObject); + + rb_define_singleton_method(mTK, "eval_cmd", tk_eval_cmd, -1); + + rb_define_singleton_method(cTK, "new", tk_s_new, -1); +} |