| allegro-cl archives 1997-2-5 | home index prev H thread prev K thread next J next L |
|
From: Matthew_Haine Subject: How to do truly multilingual ACL apps Date: 1997-2-5 17:05
Earlier I asked the list for experiences with multilingual ACL apps. I didn't
get any responses that were terribly informative, but I was able to solve the
problem myself eventually, so I'll give a bullet-point overview of the
important points. My goal was to allow the end user to be able to localize my
program (through the use of a built-in editor) without ever having to see the
code. Here is how I did it:
* Don't do any string surgery on text that will be displayed. (You never know
where characters begin and end in those pesky multibyte fonts.)
* As long as all text goes to the screen from draw-string-in-box calls,
Windows will handle all of the foreign-language fonts. Just let the user
choose the fonts, since ASCII might not be the right character set. (I've
tested this on Japanese, which is multibyte.)
* How do you know how big to make the boxes? As it turns out,
draw-string-in-box can be modified to also act as a "measure-string-in-box"
function. The code follows as risk::dsib3. DSIB is based on a call to the
windows function "drawText". Perusing MSDN reveals that drawText returns the
HEIGHT of the text and has a flag for NOT DRAWING. That's all that's needed.
* Don't hard-code displayed text in your code. But, since it can get tedious
to maintain a separate file of string-constants. You can define a character
reader macro like this:
;; Ignore the defframe macro--read it like a modified defclass macro.
(defframe named-mixin ()
((display-name1 :no-accessor? T :editable? string :watched? T)
(display-name2 :no-accessor? T :editable? string :watched? T))
:mixin? T)
(defframe text-frame (named-mixin) ())
(defmethod display-name ((frame named-mixin))
(or (slot-value frame 'display-name2)
(slot-value frame 'display-name1)))
(defun ml-lookup (token)
;; 109 bytes
(display-name (gethash token *text-frame-hash*)))
(defun |#"-reader| (stream subchar arg)
(declare (ignore subchar arg))
;; 307 bytes
;; Back up so we start at the first quote
(unread-char stream)
(let* ((str (read stream t nil))
(frame-symbol (intern str "RISK")))
(unless (gethash token *text-frame-hash*)
(make-instance 'text-frame :name frame-symbol :display-name1 str))
`(ml-lookup ',frame-symbol)))
(set-dispatch-macro-character #\# #\" #'|#"-reader|)
* Provide the user with an editor that allows the user to enter his or her own
string in the display-name2 slot. Also allow the user the ability to choose
display fonts.
;; CODE FOR DRAW-STRING-IN-BOX
(defun risk::dsib3 (stream string box
&key (h-just :left) (v-just :top) (measure? NIL)
(wrap? T) (no-clip? T) (remember? T))
(when (and remember? (not measure?) (not risk::*redrawing?*)
(risk::memorex-p stream))
(push (list box (risk::my-copy-graphics-context stream)
#'risk::dsib3 string box
:h-just h-just
:v-just v-just
:wrap? wrap?
:no-clip? no-clip?)
(risk::memory stream)))
(let* ((length (length-vector string))
(format-word
(ilogior
(if (or measure? wrap?)
DT_TOP
(case v-just
(:center DT_VCENTER)
(:bottom DT_BOTTOM)
(t DT_TOP)))
(case h-just
(:right DT_RIGHT)
(:center DT_CENTER)
(t DT_LEFT))
(if measure? DT_CALCRECT 0)
(if no-clip? DT_NOCLIP 0)
DT_NOPREFIX
(if wrap? DT_WORDBREAK DT_SINGLELINE)))
(result (ccallocate int)))
;; simple DrawText call
;; Use slot-value here since (setf dirty-p) is not yet defined
(setf (slot-value stream 'dirty-p) t)
;; due to a bug in DrawText we must set text align first
(windows-graphics-call stream SetTextAlign
#.(ilogior TA_LEFT TA_TOP ta_noupdatecp))
(windows-graphics-call stream DrawText string length box format-word
result)
;; restore text alignment
(windows-graphics-call stream SetTextAlign
#.(ilogior TA_LEFT TA_TOP TA_UPDATECP))
(cond (measure? box)
((or wrap? (not (eq :center v-just)))
(setf (box-bottom box) (+ result (box-top box))))
(t (+ result (box-top box))))
))
|