Logo Search packages:      
Sourcecode: malaga version File versions  Download package

canvas.c

/* Copyright (C) 1995 Bjoern Beutel. */

/* Description. =============================================================*/

/* Common routines for all Malaga GTK windows. */

/* Includes. ================================================================*/

#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <setjmp.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <locale.h>
#include <time.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "basic.h"
#include "scanner.h"
#include "input.h"
#include "files.h"
#include "ksc_table.h"
#include "canvas.h"

/* Constants. ===============================================================*/

enum {BRACKET_RATIO = 6}; /* Height:width ratio of angle brackets. */
enum {BUFFER_SIZE = 200}; /* Size of Hangul conversion buffer. */

#define CM (72.0 / 2.54) /* How many Postscript points make one centimeter. */
#define PAPER_WIDTH (20.9 * CM) /* Default width of paper. */
#define PAPER_HEIGHT (29.65 * CM) /* Default height of paper. */
#define PAPER_BORDER (1.5 * CM)
#define PAGE_WIDTH (PAPER_WIDTH - 2 * PAPER_BORDER)
#define PAGE_HEIGHT (PAPER_HEIGHT - 2 * PAPER_BORDER)

/*---------------------------------------------------------------------------*/

/* The character codes in the hangul font N3F for the initial consonants in a 
 * Hangul syllable, indexed by their trigem codes. */
static char initial_consonants[32] = 
{
       0, '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
  '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
  '\xb0', '\xb1', '\xb2', '\xb3', '\xb4',      0,      0,      0,
       0,      0,      0,      0,      0,      0,      0,      0,
};

/* The character codes in the hangul font N3F for the vowels in a 
 * Hangul syllable, indexed by their trigem codes. */
static char vowels[32] = 
{
  0, 0, '\xb5', '\xb6', '\xb7', '\xb8', '\xb9', '\xba',
  0, 0, '\xbb', '\xbc', '\xbd', '\xbe', '\xbf', '\xc0',
  0, 0, '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6',
  0, 0, '\xc7', '\xc8', '\xc9', '\xca',      0,      0,
};

/* The character codes in the hangul font N3F for the final consonants in a 
 * Hangul syllable, indexed by their trigem code. */
static char final_consonants[32] = 
{
       0,      0, '\xcb', '\xcc', '\xcd', '\xce', '\xcf', '\xd0',
  '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7', '\xd8',
  '\xd9', '\xda',      0, '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
  '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5',      0,      0,
};

/*---------------------------------------------------------------------------*/

/* Widths of Adobe Helvetica characters in ISO 8859-1 encoding. */
static int widths_iso88591[256] = 
{
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 355, 556, 556, 889, 667, 222, 333, 333, 389, 584, 278, 333, 278, 278,
556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556,
1015,667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556,
222, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556,
556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 278,
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 333, 333, 333, 333, 333, 333, 333, 333, 278, 333, 333, 278, 333, 333, 333,
278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333,
400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611,
667, 667, 667, 667, 667, 667,1000, 722, 667, 667, 667, 667, 278, 278, 278, 278,
722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611,
556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278,
556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500
};

/* Width of Adobe Helvetica / N3F-5 chars in Hangul PostScript Encoding. */
static int widths_hangul[256] =
{
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 355, 556, 556, 889, 667, 222, 333, 333, 389, 584, 278, 333, 278, 278,
556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556,
1015,667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556,
222, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556,
556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 278,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0, 436, 436, 602, 436, 436, 602, 436, 436, 436, 625, 436, 663, 436, 436, 687,
436, 436, 436, 436, 436,  80, 370, 446, 370, 446, 286, 459, 286, 459,  80, 370,
446, 268,  80,  80, 286, 459, 268,  80,  80, 268, 268,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
};

/*---------------------------------------------------------------------------*/

static const char hangul_ps_prolog[] = 
"%%BeginResource: font n3f-5\n"
"%\n"
"% Copyright 1996, 1998 Lee Yongjae <yjlee@cglab.snu.ac.kr>\n"
"%\n"
"% Permission to use, copy, modify, and distribute this software and its\n"
"% documentation for any purpose and without fee is hereby granted,\n"
"% provided that the above copyright notice appears in all copies and\n"
"% that both, the copyright notice and this permission notice, appear in\n"
"% supporting documentation, and that the name of the copyright holder\n"
"% is not used in advertising or publicity pertaining to distribution\n"
"% of the software without specific, written prior permission.\n"
"%\n"
"12 dict begin /FontInfo 9 dict dup begin /FullName (n3f-5) readonly def\n"
"/isFixedPitch false def /Notice (Copyright 1996 Lee Yongjae) def\n"
"/ItalicAngle 0 def /UnderlinePosition -100 def /UnderlineThickness 50 def\n"
"end readonly def /FontName /n3f-5 def /Encoding 256 array 0 1 255 {1 index\n"
"exch /.notdef put} for dup 161 /k_f1 put dup 162 /k_K put dup 163 /k_Kk put\n"
"dup 164 /k_N put dup 165 /k_T put dup 166 /k_Tt put dup 167 /k_R put dup\n"
"168 /k_M put dup 169 /k_P put dup 170 /k_Pp put dup 171 /k_S put dup 172\n"
"/k_Ss put dup 173 /k_O put dup 174 /k_C put dup 175 /k_Cc put dup 176 /k_Ch\n"
"put dup 177 /k_Kh put dup 178 /k_Th put dup 179 /k_Ph put dup 180 /k_H put\n"
"dup 181 /k_f2 put dup 182 /k_a put dup 183 /k_ae put dup 184 /k_ya put dup\n"
"185 /k_yae put dup 186 /k_eo put dup 187 /k_e put dup 188 /k_yeo put dup\n"
"189 /k_ye put dup 190 /k_o put dup 191 /k_wa put dup 192 /k_wae put dup 193\n"
"/k_oe put dup 194 /k_yo put dup 195 /k_u put dup 196 /k_weo put dup 197\n"
"/k_we put dup 198 /k_wi put dup 199 /k_yu put dup 200 /k_eu put dup 201\n"
"/k_yi put dup 202 /k_i put dup 203 /k_k put dup 204 /k_kk put dup 205 /k_ks\n"
"put dup 206 /k_n put dup 207 /k_nc put dup 208 /k_nh put dup 209 /k_t put\n"
"dup 210 /k_l put dup 211 /k_lk put dup 212 /k_lm put dup 213 /k_lp put dup\n"
"214 /k_ls put dup 215 /k_lth put dup 216 /k_lph put dup 217 /k_lh put dup\n"
"218 /k_m put dup 219 /k_p put dup 220 /k_ps put dup 221 /k_s put dup 222\n"
"/k_ss put dup 223 /k_ng put dup 224 /k_c put dup 225 /k_ch put dup 226\n"
"/k_kh put dup 227 /k_th put dup 228 /k_ph put dup 229 /k_h put readonly def\n"
"/PaintType 0 def /FontType 1 def /FontMatrix [0.001 0 0 0.001 0 0] readonly\n"
"def /FontBBox { -529 -252 703 701 } readonly def /UniqueID 4030051 def\n"
"currentdict end currentfile eexec\n"
"D9D66F633B846A989B9974B0179FC6CC445BCF7C3C3333173232E3FDBFF43949\n"
"1DB866C3EF1B30F529F56D40DA3462EACD8F8BDB057A36A23611B08B42B62E04\n"
"F78389045DBF331069C7CB96640FF89489B5599FD6BE8EF25688798EC4F2C13F\n"
"431245DFF93AA4EF41E6D82885E1C6AB7F67F4BF4B809E88F7CBB013AA0CCEF4\n"
"187B2C4653946A3B5C610D2FF1E0E7D3AB43709002F44517BE09E1C3F35D2F59\n"
"70F3A4615689AA659C58AB0C3741705267C93A5D6CE9C8D3BC8F086A7492CF58\n"
"003DE8CA89A1609FA0E9E6FFF90E99BA58F8C5D720E87110453D4959E5333F1F\n"
"2B383165401D07AC9CB68B69A6C2E343001EA94177A6EFE9F6CB1DB741B84D6B\n"
"BD1E84F5175262D86AB8AB478840A805BA8BD467219CD28A5C4B3820CD173783\n"
"258C58123D0077CD375CFF73E250093D72C85E8EDE7FEE4B09F1EED41C10705D\n"
"FEA2ACC563D8471B4B266DE8E655F92D3620EEF32EB23D83FB199B98894CD857\n"
"7E035EA9C44A817375FCB4A9BCE3370EF65EE1F74A86B4EFAC30F091D4D34A3C\n"
"EEE0BF8897E6C79DF68ACE676F3F2145304CF7E6339D1EE856ADE3DFB7F9D696\n"
"2E7F000C17B4E1404C86D34ADA528F30CB522C18A00B709D05B77EF2BDD58B88\n"
"2B96C6623112C67849619CA63CB8AC370617E70FFC7FBA86E5DFFBC4B2EE6062\n"
"E701743060851BD8DB9DB790793CBF2DCEE3E5A755CFF7FCB5EB90E05899DEF7\n"
"EA3BFD6B1B6163488E883065B81AE93C4214F0F0CD360B84B337483286EC37DD\n"
"52E8DBCD28B403DDBA54E355721B6EBA71EF5B565D43BC79FD6A051673CFDBED\n"
"BEF9342B3C9157EEE4954566F87AF445C8A6CC6E5537429B2527C5F3FC38108E\n"
"BDE872FEE3F26A17EFDBB9D73B43B65CB13E24100DFA006BEB1832B9C45C642F\n"
"239155ED4ED43F0782AF636F17EB20BE8C8967E283DC6A00B15EAF8E8D239C29\n"
"944F60CB5DE69B4DF9F56EE75D274C897DB3772D7CD0E1275E30C73F0C023560\n"
"917768FE34D77039619F153CD8AC0826DF1C685DCFA7A6688008E0411FCB6EC1\n"
"5CFA71301D5162A04BC7B715282F4CC8EA0A5E7D36665DF8136D37227F139DC7\n"
"0CBFBCA56669AE03A0F69B23E7D313BB70F754419AFD2E9E8B6E2ED827D71773\n"
"24612554F335CBE16973BC44C1AADE6C2716B284777A2486BFEA7ABEE1D2E4BB\n"
"8E4FF2F56DCF2767A73599268B055E856549A52A6A775C61C4A02C6AAEF3C444\n"
"C709146DE1678608F5D46A50C126DC5E4161A67916D828E9BD8D3BEA2A834527\n"
"CEF127D99ADADE3895DFC60447332E0E34D9F34C544FCABCE014DB9E0469E1B6\n"
"9E32205941840B9BF973B5A70CFD1D2A14DAA1159517D180565ABDAB2CFB296B\n"
"AFB72AB045423ED9094F0E300B7C06E1D5C32E7315A354033E02EAAFB0BBB4E6\n"
"E4990C5C0C9D40406608B43835C76463EC08FBBF5A3742EB2AB231DC2B3DC3C0\n"
"BD7C9793C9A12FA4200E6F59DD7ABAD4C05684D1136FD2EB7ECA405596E23784\n"
"C73A815F3EFADDDE43C92BA6B1776BA381423C731F10D7F85D5853A8C9124183\n"
"EFC63F8BEC944AE48AD4917F51ED03F87755CAB6FFF549BE13F92BE50B4806B3\n"
"16A5DAD7A6F4D845E853C1CC115AB7E455293A1C911AACD1457282AF25336FDB\n"
"B94A55F79F11B7C95CA81494697BF72C8866CAD79633F0154B6D3DBADB7EE005\n"
"2A0EF550284490DD1B034946E245FC2CC1EFD485D1F376AEBDCFF96A838C3ECE\n"
"4318658747C18B728784C78289C86891300D53F4EAD5A0CF90DF91632AB92FE4\n"
"1BCD86336AF3DD0936B0CE6E4ADF70BFB767DC29F05E7F874A6366D8E63DA571\n"
"089C6C9C49625CE9FFFBB596CE078D175A6213800E59218D7B8DBFC0640CC191\n"
"DC5F095B7BD881889D00BA5B8010785626C470C7AAD7D60C054C4FF2EFE9DAEF\n"
"997FBAAD4A5C6567838A49B6634BE4AAB05A0806CAC18777E87E0DC6B078F866\n"
"E40537462E96004AC6097ED2E47A9725EF80C77DE8AE7898282200D23FCC3337\n"
"A4CDB738445AF479009B14ADEB95B2558DEF31712B8915CBD959AFC8F8390A5A\n"
"B178F926A57F6C63E84658AB33FCD3D41F3209877ADF46AD8C907C14882397C8\n"
"2B2D5A7936D0B92C149ED8A751916471BA052D1302EC62EA274F04AA667821A4\n"
"3533DCE8DD20901983D574283A68683D07E37FE59965710F8CAADCB7681DE258\n"
"E59CAB0B1793F67F686A311296B2BE7A7D10C868F8BD39A1CEF857B8A4E027FC\n"
"97BC55F1C9B2444C04C175350218C60C1944500391DDC69E7FE4455A753A66DC\n"
"E85044D657B2D71AFFBA2DB53C70BD06357B687672C78B629AECF6E7C406ED52\n"
"5539D9E2C1E7E7EB5B27A206164F06D850A1BC5E92C2CE65C0D3037EC911E440\n"
"1C738E8824D2B910C020E1650D5853A7B5D492EC1AE1E1D5C5DB26D5777F11E4\n"
"344AB9F87997D46C86C6145590C912FA35AFA2F3F96E3AE2D6A78CE53E4F1F9A\n"
"F39EDAC046ACCC5A0AB801FC11C98BDC61D689263040E9633EC3779A4D537448\n"
"F805224DC4A9CD3D1353E0D662865A0148B1C3DAC0987B5735DABF8BFF7A7084\n"
"F47978869D6CBE9505E58ED8D56784FDA1722B76E87D4DEF3C3BA4483F8AF555\n"
"ECD0017C1CBDA13C74FA98B6F1F85185404D5A37B3EA1380BB1F3DFCB977B294\n"
"3CC6EBF8C4018D782D96671D2AA125E3DF8D9AE4BAFC09B85DBDA44FA7CD6493\n"
"9589D3A258954FE2473F133AB1BDCBDFD79546E823395CDD248410C5D5CADF40\n"
"47A5758106F0A92B0AA87F5C548D791A2594A1AAC4AFE84A91DA057724857615\n"
"66633131C9A41B7C8315F04B97FBFC80B2D621F88DF6566613FCC27A08222546\n"
"6EB0859B060B93EBECE5F69976BAD8BBDB1DCF36DE6A653942A3FE1B58FE88C1\n"
"865EA81AEB3945B35AF29B6CAB2943F40936B43110376409CCE7A2BFA1A618CA\n"
"E03BA0390E6DA539C6FB4CFE82CD5ED290505C33BDA643254305E3C27288F2AE\n"
"0D22BC2C35452E5827BC119154D0953543663C4AFE36C00EF6402370E3D8C0D1\n"
"077BBA2BAB79EE3C7C8F70853EDE9E85DF996674F7A5883F3C763BAE63BB22BD\n"
"180AE2E1A7E787CE80784EBFD0240E595F9BC3D9750448E870413E0ECAB69004\n"
"03DF2D305F6EB4361942E75B0804F1609B5DE3F2C2D4A6C9543EC20F823A87A5\n"
"F695983F3FBC101C5000277D81F1EC4ADC2F105E06BC5EB528C399D138572C57\n"
"E8DC7A6F41DC6F8D44534388C30BFA963C52BC2F2F786C69BBBD405C7904ADE6\n"
"251C08F462A33A1D7D65D954299921053A87C2D8AAC2BDF9D713AD90608C6420\n"
"48AD52351B15C2211F17867244284964870D00A19B0B2378F28E232938D0DBC4\n"
"48B5FAC2207C17B6BAF3CE9EB82A315354A7EC183226EAC400067E06246BA400\n"
"B64212899C7049F4C13A8C079C2A6EA22DDFE73F4EA683B053ED02837BFA669E\n"
"BE87C0FBE2FD749D2BBB01CF22A8CBB3F4D543649B45E397F1F3ADAA57F98A3A\n"
"0F5ACF4F92541FF903DB28F9A29EB0AAE46430083913B3CD6FAA042D2EEC48A7\n"
"79CF038443F660845621E68CA56BAD5DF203A559CF770E515B726149F1821E38\n"
"017F04CF73C9B146C1BAF29BC0EA6FCB06FF083201E09C67F16441C37609FF27\n"
"5933986E314AC7FEB4CB45523CEC5F46F100D39BA02949F3F05AB76BC5E2F4FD\n"
"B91B6EFCD17E2123B260B3B954BE200FE9A6A1AEF77D8BA16CC44E3475D15289\n"
"8A6FA3D19D4CE4F3DC0325E40237647A9E2C053CBE4BEFF24F6B2E00FDA93593\n"
"176F94639A40546722168D68B5608F8CE798FC5F84D7684ABE805BAD9D8B9AF5\n"
"989C2233956A4DA479921ECBD922F882D0A94F9CF67457FA73673B65912D8410\n"
"7AC025DF0D2DDC21453C5D251505F5CD0E268C7421AD668A33C66A524B37DCAA\n"
"2F7C5557099D723FED2C7964E11D857760CEAE192FC30D685D599196324B29D9\n"
"E3A44C3448091957AD6429165A7F406E39C138680C0805D54A3CF15028EA725D\n"
"E88FA056E89108C0C1D23CA3C6A71B21B0EBA517D6FE77079B7088C0FC900101\n"
"CB2191D9ECA7A8B82B6F6DAE2FDB01EE67809232BC5C0BB2C26E828B4A6A3A30\n"
"2510326780A7A6FD6486ADD2BF76C24821791D5E1F36BAD6749A8DB20F925CFE\n"
"8D7BCF9F9EFBB8E09A48DDFF0D622E76F4F1C5A400918673FB13C241941CB237\n"
"FB2231A5C87D5B243DDB58AF892CA939AF35994BC6C9CF52972832FEC6D3B1D6\n"
"BEBB1D1D06D98F44F435108FE3E43596E5A84141609EA0C4DF97A1BF4B117EFF\n"
"B7952B3A022893E8356F8E2583B258E6738DEC7AB8EB78C35CE04495D1196156\n"
"A9829343464576E626889251A472BA52A3C8ADA9646B99EE75DC5207C54938D2\n"
"39E04F381C44D067F935426F4690FE1B6674FC367EBD5E93D151AD7880324F9B\n"
"AAD544F08F3E5911A1A47B0C2BE61C0A988D8E3E7DC05FC9C3311AE4F7E57FA7\n"
"316AFC4A7383A4C55555ABD374995B938AB0B69089420117605137B612737288\n"
"81007C3444CA68D4EAE0E0A1A240B708697456B440BCD7B598BFA428D5CD28E8\n"
"F96BA3A9D4A0C2728E89AFE02D14D1874639E163C09C71398C891CBEF3B8BA6D\n"
"CDE95EB756B8ACB762875E559267EE8EC451A3CF7E95A1D4A9EE33C87AF036FC\n"
"BFC7D0B825D86C9C277BB42D415C6D44C4F45A14320C779C1A4B0E13347B0001\n"
"1DA60DEE09061F725479C403EA4973C6D450EB15EA6D033985AAC2592B012AF6\n"
"8D2B854FFDF9BB03EC14805E251300760AD41804A8FA2821CA77794D150484EE\n"
"EA58CBB43951DC00B9493B6DC15E200BF394E19A446DBABFF71BD75CAE3D6476\n"
"E0CD659214F462A66BD348C81F97A31C3E4B8078223F00D36EC25E48D65CE127\n"
"DF09839B9FFCDE6055FC4CDB136126CFE4891C0BF7DAF9D582378845361EDDF4\n"
"93BDD1954DE5DBA70B98C462744FDE3DDDD6719BB00FF4A01136C36D20D00951\n"
"A7EB060BAEB0965A18CAD664E6DCFAA98F496704DAA6FE0787D934FFACB0E861\n"
"9753ED6BF44106206855C9E313BDC268208769E8FDD864A2A75DFFA213C0A49E\n"
"C4386F1E55F5FBD43E0AD5B3EF46531D7124D22F5080220D7DC6FF9D2E03DAF1\n"
"797544ACB18E060A04A5ABFAC6E20B8B374D606D555B6560EBCD7044B78D38DC\n"
"AAE19EEBA625224F9E016D49B6A5710CC3A0871BD3CC00265CD8E67109F7EB61\n"
"550A41FE60D2474F961264D218E302CC35205A1D655F878C8FB831DCAA33E134\n"
"70E2D2C49EAB90176513B2D1A3DB731D7207F070AAAF90B7990BA77FB559221A\n"
"855D62E824F18DF1A63DA56813CC79C4B990E87E41C4E8A9A6244E108D94D48D\n"
"A8079D448A916F68A39B5ACB375AF61C52725B1361A0FBC5818A0052F4F0B1CA\n"
"FB11446E47D6177318E3F83511BFDCE237FE94BF1223C29EF05BC57E1668A7EE\n"
"C4D9B5CAEBF4F14ABCF26719D0863FDB73DB4897E02C995DEEE70CEA14C35542\n"
"6D508B3CDD8F8A10A78F6219991D2133D77F6D6D4D346E2207F29478D8F071ED\n"
"73B331A0D41DE6B15355F85ED54AA37C502D63B9090D53F9B025877225E6FB88\n"
"6A1809D536F1F096DE2FADC386926BC07421BD0C49A01C4D77D4627BCD349528\n"
"97FC9A0D7FF60C5B705E2B1A669C938733E30BC55F03476A6B8B5CD03C3150B8\n"
"1236D235FE1E7EAFFF2FB5DFF9980328ECE0B627B4C178B6A95A4CF197611748\n"
"959878A68FBDD8857205B75524512B896357FDA8D464244646018F6078B687E3\n"
"7372A3E9792BDD3435302CB509E728BCD7B7377F2E0D5C02040033D9113AF22B\n"
"BE956919AD2CA332C1FE4B5E4D6741523A884E222FA8EFDC1C24FBBDEEF6465D\n"
"65D8A0E2C334DD806062ECB16A5BF6D24221141A2ACA31EF8AA7BA7A10ADED5C\n"
"9CC618E69EA66BE2A7A625970751BD86412514DA8441A1DBF6F0D49112A5FF88\n"
"95872BC5B995CEF7D3A27936B17E134AA66CDC3583DBA0678DFE12DFAC7514B9\n"
"52D46F914F73AB0208DB3CDDE326FFF7293CA9E3C305F8275B78E7870A50BB50\n"
"874E93D2D26A15F0FFE0D74345E536AD39ECDA53FFDA5A625001C26C97FC6FAF\n"
"F0864403FCB78C2A7EC1727DAD5BD64762086DCF5A17D4E9035123FA43C1630C\n"
"69BB2E10FAF95973AE38A13BF57A5BE8E579A43E618D4DBCAC9075D89E6CF5FF\n"
"F0DEBB17E452D336CC87DB03A3C872BC63A11F5061F7E112723E1C89ACAC5679\n"
"E7D98AA82A939FCD42CFE1BF81E87FA66FB3DEE9006B13E339FD9E034C485FA5\n"
"04812788EA8CB1A932C94645D26528CE15408B526E8EF98B1CE8363C5FC47054\n"
"8065293A984D7BB9A21F99D4BBD1B78A09FDFB18A6E8B2944D6C8D52A20E3E5F\n"
"71FDFBEB31B570D0F9D7C265338A6C5E9E0CBA742A01208D560B8FCAF15BE825\n"
"ED294D9613FCC511B62612EE6A4F35EC370A57DCC608963A73197C159FD9EB5B\n"
"F0F0AB9A86ACE51BB0C1F89029393712E1F5EFF2335512803C27524ABA13AB32\n"
"F1593B11A4996B133DD81051479A49A2459D223858A48ABBAB17E4E0B0EEE26B\n"
"0EE3EF75772268FE26F6441CE2917D18EC251DCAF7D2554B2F955FFE606957AE\n"
"2F86B7AE7CE8279638A0A3EDC3FABA38F24C4904B75262210FAC02D7A71DA8C8\n"
"C07946A02F8C24DD5A54F751A65E0ECA012F4C80C13C0AE59120AF0790B004F1\n"
"4D479EFBD046EE049B12EF9FEC70DD8196DE145A7313058A292CC7E360288C28\n"
"A596430C9B9F6A06E1C37DCAB3C9154F1F228778D77AA82126AC31F860667182\n"
"A74FF150224860B76429E3BE635A4A93CA6DDEA12E7E34501992F31DA9C55D43\n"
"214DE92977FB118F2523123FCF45C6451FAA16447108902A96C05914FC82A62A\n"
"349B3477F67C1CBCA1B3D135A649CCBC9D7BD3931EC402CAFF93974836A8E5A5\n"
"00F251261273FDE7649955FE45EA108D4B3C3895EB1E1EBC4B39DFBCAD92A245\n"
"75B09F873B851907139E6D430F4F928111414559B2FDB535DB222F8EED030F48\n"
"27EEFE50F57597190CC790B3B1B55F21433D8DC831F30B2B0F70FBDA4160F94B\n"
"36ED20D77E577E9250875DC6FC88BF280639842556AF84FF7538DCFF36D5536B\n"
"9FC9B0EFC405FF23679BC1E39FBFBCB428D5F209562BE553750DC8CD6B9F6FB6\n"
"BA9655CAEDCDE73757DEEE940C4F4FF04CA455AE8C3D94432FF9874872B77C20\n"
"71F46663493F8ACFED7B8F1C33C5C19B139A0460CBB676F523458270BA4393B9\n"
"EFFD55BD5074FB63F90827F5C9C46C9DDFA4B84AD0F48807094FC847F51C64D5\n"
"E1224E16AD882A0ED6C647E0FA2A0BBBD19F461FDA5A255BAF014599DAA755E1\n"
"18C52E7851D6651DDDD638EF488113772D52C41E135D4A4B587359C88F1BFBCB\n"
"C7925A384EFCCB35A8EA112AC298D30263A3E9A857949EE4E04BD278BEE68307\n"
"D1B351F54A233AE3FCACF2947FF6E949E6C391C24DC2CEEE5267221C30AD63C7\n"
"2E09DAAB39B2A33BD4FF51441CDAB009D0F30EED102B36C77AD3EF4C30233C61\n"
"D1ABDB13B7C9EF211DC7003B3A848DBAA7573778CC2518D8DD872106182914D0\n"
"A8E4156D99481972BDA413F9FF42BDF74BE29A49EEAD55804BD5BBB594A0D250\n"
"4B5898EE8FA5A029D850B0C6582E9EEB89B17902A94FBCBD637503C0BF334681\n"
"2BDA2CED91F072946FDE2AA824B824E628D4A229C63C8ADB8F235B77AFE501FB\n"
"91DD3B8D226DC0E69F9F68F12492F7659CB8BCC352DA9DBBC417914F6C8A291A\n"
"3EC92956A937FA07D337240B7946C311731F681281B772E330CCF5DC918D0F51\n"
"EFF203DB777EBC6267D218469675DDAD79774D0E7BCBB3C2196D8D264BC6E5CD\n"
"6E3FE1DFE75A6CDF53FB3D889A723DE8017EAD77C1E67853F9A45B091B2C2A5A\n"
"3FFB3C318024E34632220F1B2A470A2A00A1C28D5B61B8ABD95B060021D149A7\n"
"A765D00C76D38BE872BDC55EE8C43A2F98FB291ACCBA870FCAB713ED4F6D76A4\n"
"B9CB086E3B8CF4E0F1F51D43A16F56EE69373CA6D6D6AAD8B0D4DB1CF83505F5\n"
"E54133E9939074D9DE2CE82AC1F8469AFF9E6C1E43C32DC2A12367CE38141616\n"
"4726CE8AD13EF9F07DF45108F6DDD16677DAA5250C9351DCAD937E060D0E12A3\n"
"FD0858C4C2961C1E9E0DBD611B661400E40011BCF0E9EA20EA056685502399C8\n"
"4E0113757D35D7F01B753FFAF38560E6BCDAF5AF9483266645A00BC17711AC1E\n"
"D4B504AAB85349A31BD73EA6516F069A9CBF808D16EEAE065B6BED42DC799904\n"
"A9DC3B361557973754ED0FA022D82FFDBC454865A12A68216338F1BACBF8A9E7\n"
"492DB4518DEF56FDC0AE0BA3A2A20EEF538DA7804F1F223683638E1920F28756\n"
"D4926C7B9133D332502C7EE7F71EB00715704E0162ED8E1A0422FCD864F71600\n"
"CA2D1D740D45FF4E0D1235B143D1F690A019E8A57C46357207C4F7DD81747392\n"
"ADC44AEE8CB7586D7C4B4CB63753C5EB9271E27D34E6AED844212E8C203F2282\n"
"E79E8F25C16037BF85AD0DBA4E594DBC82C2FDB4B6973C4D470EE402A19B527E\n"
"BC61FC9D96C68B504CEC3406A159E2C8B6F2E79BCA75F51BBF4B61C8F5A21C56\n"
"DEADECDFAD4347200532497D3FE6FBA8E9BEC3D503A97CA50BA52CA242F6CDB1\n"
"90B6A5257BA4D9D18ABABFDABB4A65E1D5D3F6A6764D44D7CB6BF4AC888EB728\n"
"6E396FE80939AC26C8AD2A3DF3F3EEFA38544FA28F36E577B01E944E499014C5\n"
"A0F8B593151832FE49DF1EB8412012585DB9D057FBD60967893EE00D413C9FFD\n"
"3076E00DE4ECF271D390655C6C661141D894DA2362D9117D21C492F43728CA7E\n"
"8FAD31601D16CB92A7C53A4D388753E391106BD52C8FC44F364E75FB0B38895A\n"
"C67698F992012379A68AD2CD12679D5A88973B0EA80C8CD163918D8B5E9C18A0\n"
"ACB813099EC4CF63FBA34E5A1F9AF41C597FFC7843DB506CED639A1B56E77D26\n"
"3F076D4391E96A1E38D7E7F9279E7593B2E9E15FE3DB0D0F3629304D914FB012\n"
"EFD7628E50A1E9A62FDE38E67482CB5338880ACA34765DA71E7B3176F235365C\n"
"EC6F60B7201065B459AFD536A9D1605B835793B34646484E7E500A63DF569987\n"
"FB00E458300019B681E1B38DEB754C2A611E872ADEE0C300AC04341CD0410F6F\n"
"165E1004EEBA0A52DCDADB4C86ABB50529A38A509BE89A4E2A35AA7899F964F3\n"
"61297AA16F717BC2BDD79C23851618DD8FE9C15FA740A0F9D42FB8E620AF461A\n"
"8F9744F0D92888ED96B86AE357930D0D9D2DC8917D74B4F2EED482AABEF9EC6D\n"
"C1CCE9796830ED60FD27435F1280894A94C0EDD2551C1CED18B9BC713CB17537\n"
"DD797DCD049D65F9D2826AE6E19BFD3105E95E4E1B747FAE3877EC12F998A267\n"
"69E7E783FA612E70F43F551F66AB8B1F6B93B8F2A62A7EC67FA00F56EF464020\n"
"43E6286D0A40F4401DF7A6958679205B9DB58DE63FCFBD23C171F010419DB21B\n"
"42C2CE7D3F34ABD4A196A75C46363DB71CDA9CC04BF4046DE27F5CD9CF8E74B9\n"
"F440DE40658E62000A9A20F5ADD6213DA6B0915F5306BC4C9824B8E0BAC64ADE\n"
"88C1515DCCCD81CCB83E8E2E59655177EBE4DAC666C71BC21A8599BB2F533FE8\n"
"44DB822B632AA8B85D7B938C89717B13F9EC0C4975B2404389CF9B4D40DB1A73\n"
"701F3CF8FDB977AD919493746DF8952F9590E6FA4DAA82C37254BF300B65A9BC\n"
"7FB93762E42C0856C4231BB1F04A03F29FB8E70B4E909372F8F1496BA47CA82F\n"
"FBF3808CF096B1D5D7ADE9AED8872E6E8B3791311AFBB5DE7D0D8C3B0D8E9243\n"
"3BE02526125EF34F0A575E0E789B2038DF2F4ED5182EBD5CF5A9AE771F8972F7\n"
"10B6AF7BA5F0810D4E0F01465FB4A4C25F916B60CA5BA7DDE92CB24D0DE0F713\n"
"A963530BB2EA13F54C1C0FBF8D9BCB94C781FFEAEE6AE48BC951317BAB88B140\n"
"91343ACE736604B473CFC14A1845D648737C47B42EE4286CD7156C987704CEEF\n"
"FD2BFBDC4B63CE2B633C09ACC51FBEC2F8B64E815730465F5F61C1E15A1A8632\n"
"D6E55EE400D6A875D4CAF9839E53E3C28C99EBAB7E0B523D44B1270FD41FB736\n"
"1DDB0EF87AEBF2F2CE2B6DBCFDAB74A65D80E82D08F2C5F0F98F84796666FAA1\n"
"3C9399831AFA98B6D2FC37FE47C2C24E30020D85C7CB03F4EB85A6904AABB074\n"
"6E916B55F27832DD190335B88E31BE657E560A847E1E6C21CD85B2EDAF8726F9\n"
"5A1856B8D26969145B9F60E61254C57EDA7827896FFBFFC4C35F338F22175278\n"
"09C5D48862BBD55F1E4ECA3A2C6092D55D4E4AAB3542CAD1B77B2338314C7102\n"
"89BCE74F362C477ADECA09368945A337636DB7A5AED30DD64E3F3D4D1668492B\n"
"C40F67940FC90E5E64DFFBC97B23E130D387FF6FA785515AB8116FBAE382196F\n"
"B546F8B447217FD21133C1D0DAC1F45893F2A5ADFE29E73A444EC718603D0077\n"
"E69FC22A22849EBA5727C6A05FECDE20A1DE131A6D4DA9A5408147B23C320366\n"
"8D4C64678E2B71F757FF0E177219D3750F6A74A025ED156BFE446BBA3194ECD4\n"
"C71444B4E63FA0DAA81908C395C77C6484CC26E3E52D3E1D8E341C8366338254\n"
"FAEA45B6E32312C878F870B059B897F7D55F89FBE6427F40DF26310FED372B87\n"
"52E15FFA84287EB0740874FD97D090FA566FDED66372FA95D1C75DA5F2A02D42\n"
"2CED62419A1CA43FC1AA0B806866E07366BDAF329651C088EAB26D1F19A95D19\n"
"2BB46558622D6777BBC2DF431E9457A0F617B77A87F7414759FFFD66E00A16DB\n"
"9B24E997416A1BEF302768E81E252903B975275A468EC0A247BA670D8B209534\n"
"2B734D767266E2241384B5893288A0D50E12C851A47990A2BAF2445BFB7C8852\n"
"F2BA046AC935C119C3C6542AD6B5C339E11FCB55EB12BF753BB43F99E4B48317\n"
"4C3F2A0530B5AF0D89910CFBCA2290920EA1ACB34951D175D0AEF4FEAA62CE93\n"
"51F5C0ED55F0292D7F28E033D0ED773C5C5AD3E47D7DFE86C76B0C7A895F9DB6\n"
"880FE08C2008DD3C2C88FA04ABAD5D3D445ABDB5860FB48C0B973C251B138EC2\n"
"7FA2CE7F55BF4196D06AEE270858E9521D9E085C58F7561388650BEACCBC2687\n"
"C69BBDA32CFDB923EF5052D333E9BBC3549AD32CDD53F04C7D8CA21751B47ABD\n"
"C1C60A0DC155CD068EECEE0E7CA67CD539D85F2D8489C6C8F1CD0CEE611709D9\n"
"A7FACD511BF663667398082BF096D13665A1502EEDB5F0EAF00264F8A803B2B0\n"
"E47D4B4FEFF6761C382D73D2B430CA80207D8321A0A2EAD8B67EE5804022BE6C\n"
"5235002A07AFE2D1A77D467B45FD7BBFA5BD3CB3309FC140E2D55178A091A33F\n"
"5A9767BE25067AA7E12BC25AA6949F9DA0F81FB5311DF81363E117B3A383342F\n"
"3E0F4462676BE061AE4716D923B2C7BAA284A247842F6BF6E7D3901D3D3BF7A7\n"
"B4654D145D95B900EDB009C33540D3F5A4A1AE94E6BCAA3C85AC14CC197B467A\n"
"9E43E0773EBC4FD5FF73433A26C02CEEA99F515F5C15C019FA816DFB021FDCB1\n"
"047A79234BAF4952A0CC48EAED0A62EFCB4EFCCB72D4C9DF8A8803650F8543FD\n"
"714D869C0F1AC1CF5FAC20503A072372D922B9C6F14FA5D379306059C9FAF228\n"
"6AE1AAB6C87AD689AC039C11A6E74C8B7B726F671FF0BA4850FC9E06340D39A3\n"
"66A9D572B0A6A029F95B82787D2F9F40114C4B90B763DD6169678C53AFE0FB0E\n"
"105C77449EAF6F95C5C7AB06F03F3790B93617786D8AA01D92DC2F97D42CC6F1\n"
"BC68E73510AE2F22A5953493207D26117B4933BAFB8311F4298A467DEBABBC29\n"
"871532FA9C166056FB144016D341B0A864FB882C4114E7157E24FE046D5B65E7\n"
"5879DF87597BEB6224EFD2910C206589CC828C1690F40A75A99CEDB51B46B67A\n"
"C337BE8AA78C66E9652EE5C6281A0AD6432017570EBD8E9524A388360399B051\n"
"7E37544EFA2DB5A87CC52245494C8FA19844506585D3F3868934D5775F4F97C2\n"
"492FCFBE08E468BDF0E7765197104FBC00D963D0E9E0D778BAEF2C15CDCC4D6E\n"
"12E197FED3C2022A091D65D3F9F5302E7E9A280CC8A09388DD366D5083F659AF\n"
"73FB2834544914212D78ED6384EA96841ED737061D7AB0FA5B4381A38CD3EC13\n"
"AAA4E19C408992F2FC189B03839B38DC15A08305609C49C0A8C2E958DF933470\n"
"D535E5266F746FF5B5B4CD8FD3467B5AA1A1F4601E1532CA93FF14072DF2E7ED\n"
"A154A191E27D68B61D026FF5DF0AA39C398744DBF04FA587B226337D77EF11F2\n"
"2457472DC1C99E84D2E42C71A2E313EFF7E8AC506CC2BD5B585D0D58E9431DD4\n"
"F9758152794FB6D27F494AEBD3EF4250F24FF01924C3F18874F5AA64A532C8FF\n"
"77AF4A8641DA35622B07D4F964862F0B043C54555723887C5E67E192C28B2D9A\n"
"ACDE51F21B4D09D74AEA2A2B40769C4BA5F23B46E1415229CDD439007F6DED01\n"
"B4334AA5A23FF4A40D5308EA52EE0DEA1F5CEA92F9AE8A4437447FEE9464AAE6\n"
"3FDD49629AF6ABBDE4F38FC2FB9C9D152C0F64F64678D5100DC9E5C3A49FDB6B\n"
"5A56DB67D5126812B1D9269E6E9AC2A723AE9B296645459EF000FA9252EC9228\n"
"9DB18FF27595D6E952D75AC5A5DC3A48D7F40265280E263DF90E35B8F6E081B6\n"
"00633552A5BFCD6A526A46A89F8A1D7FF4F7FFABBBE1F6F4B80651B21F78B391\n"
"84B46A49F7404E8EEDC3F0B710EB231CC2CE0A8C81E27C01B1210F0E05FEB4E8\n"
"69D480F80DE0F4A1F4FD719902E82F2EDD0CEBDC764800F54D0594363E2BB797\n"
"82C1DC01960B76A9CF4B84966DD2D1E75EE1393B0ABD4E48462A195BF28B7C48\n"
"A2DFCD4163B981E785D06104F46B20F949B033C51AD5FAEE7CB3894C29E30797\n"
"FE5E6FF8C455A869DDE04686C30F8CAFF428AB5BFBAF276D90DE79A4B86455D8\n"
"8D94BF8D436BA46330A18BF55B393C926F1947AB996FC46D22CBD931427A4E81\n"
"1009C4EE608BC868A843DD888690530E1F8FADA75FC7CC0962C1DBFDEE630DCE\n"
"0BD7DC763F802548E813AE4394FC6B5CE560BF122A7D5593092C99010684070E\n"
"354201416C5E6B9E4A7F804FC89F3E50A70B6A2644329C7BCE70D2E6ABEAC786\n"
"4F2AA7361B71D289AE0EA5E11D6B8964B6AB1B03A455E775BC051BB7B6846CC1\n"
"6C292406018A00249C21CD1357EDDBFF\n"
"0000000000000000000000000000000000000000000000000000000000000000\n"
"0000000000000000000000000000000000000000000000000000000000000000\n"
"0000000000000000000000000000000000000000000000000000000000000000\n"
"0000000000000000000000000000000000000000000000000000000000000000\n"
"0000000000000000000000000000000000000000000000000000000000000000\n"
"0000000000000000000000000000000000000000000000000000000000000000\n"
"0000000000000000000000000000000000000000000000000000000000000000\n"
"cleartomark\n"
"%%EndResource\n";

/* Macros. ==================================================================*/

#define INITIAL_CONSONANT(trigem) (initial_consonants[ ((trigem) >> 10) & 31 ])

#define VOWEL(trigem) (vowels[ ((trigem) >> 5) & 31 ])

#define FINAL_CONSONANT(trigem) (final_consonants[ (trigem) & 31 ])

/* Types. ===================================================================*/

typedef enum {GSTRING_TYPE, GRECORD_TYPE, GLIST_TYPE} gvalue_type_t;
/* Type of a node in "gvalue_t". */

typedef struct value /* A Malaga value in "malshow". */
{ 
  struct value *next; /* Next element if value is record or list element. */
  struct value *first; /* First element if value is record or list. */
  gvalue_type_t value_type; /* Type of this value. */
  string_t string; /* String (for GSTRING_TYPE). */
  string_t attribute; /* Attribute name (if record element). */
  int_t attribute_width; /* Width of attribute. */
  int_t ascent; /* Distance between top and baseline. */
  int_t width, height; /* Size of value. */
} gvalue_t;

typedef struct /* Implementation type of "pos_value_t". */
{ 
  int_t x, y, width, height, ascent;
  /* Private items follow. */
  gvalue_t *value;
} _pos_value_t;

typedef struct /* Implementation type of "pos_string_t". */
{ 
  int_t x, y, width;
  /* Private items follow. */
  string_t string;
} _pos_string_t;

struct canvas 
{ 
  GtkWidget *window; /* Toplevel window for this canvas. */
  GtkWidget *draw_area; /* Drawing area widget. */
  GtkWidget *file_selection;
  GtkWidget *hscrollbar, *vscrollbar; /* Needed for "adjust_canvas". */
  GtkAdjustment *hadjust, *vadjust;
  string_t ps_file_name; /* Default file name for FILE_SELECTION. */
  int_t width, height; /* Current size of canvas. */
  int_t area_width, area_height; /* Size of DRAW_AREA. */
  int_t x, y; /* Upper left corner of canvas in drawing area. */
  expose_func_t expose;
  configure_func_t configure;
  close_func_t close;
  mouse_func_t mouse_event;
  GdkFont *font; /* Font used to draw. */
#ifdef UNIX
  GdkFont *hangul_font; /* Font used to draw hangul characters. */
#endif
  int_t font_size; /* Selected font size. */
  int_t font_ascent; /* Ascent of FONT (and HANGUL_FONT). */
  int_t font_height; /* Height of a line in FONT (and HANGUL_FONT). */
  int_t space_width; /* Width of a space in FONT. */
  int_t comma_width; /* Width of a comma in FONT. */
  int_t border_width; /* Width of record border. */
  int_t border_height; /* Height of list and record borders. */
  bool_t hanging; /* TRUE if values are "hanging down" from baseline. */
  bool_t alternate_cursor; /* TRUE if alternate cursor is displayed. */
};

/* Global variables. ========================================================*/

bool_t hangul;
string_t char_set, font_name;
int_t font_size;

/* Variables. ===============================================================*/

static char_t hangul_string[ BUFFER_SIZE ]; /* Converted KSC string. */
static rectangle_t area; /* Area where we currently draw into. */
static GdkDrawable *pixmap; /* Pixmap for double buffering. */
static int_t pixmap_width, pixmap_height; /* Dimensions of that pixmap. */
static GdkGC *gc; /* Graphics context used to draw. */
static GdkColor colors[3]; /* BLACK, WHITE and RED. */
static GdkCursor *alternate_cursor;

static FILE *ps_stream; /* Stream used for PostScript output. */

static bool_t ps_mode; /* TRUE => draw into PS_STREAM; 
                  * FALSE => draw into PIXMAP. */

/* Forward declarations. ====================================================*/

static gvalue_t *parse_value( void );

/* Parsing Malaga values. ===================================================*/

static void 
free_value( gvalue_t **value_p )
/* Free *VALUE_P. */
{
  gvalue_t *value, *next_value;

  for (value = *value_p; value != NULL; value = next_value) 
  { 
    next_value = value->next;
    free_value( &value->first );
    free_mem( &value->attribute );
    free_mem( &value->string );
    free_mem( &value );
  }
  *value_p = NULL;
}

/*---------------------------------------------------------------------------*/

static string_t
parse_symbol( void )
/* Parse a symbol and return it. */
{
  string_t symbol;
   
  test_token( TOK_IDENT );
  symbol = new_string( token_name, NULL );
  read_next_token();
  return symbol;
}

/*---------------------------------------------------------------------------*/

static gvalue_t *
parse_attribute_value_pair( void )
/* Parse an attribute-value pair and return a pointer to it. */
{
  string_t attribute;
  gvalue_t *new_value;
  
  if (next_token == '(') /* Read a hidden attribute. */
  { 
    read_next_token();
    attribute = parse_symbol();
    parse_token( ')' );
    new_value = new_mem( sizeof( gvalue_t ) );
    new_value->value_type = GSTRING_TYPE;
    new_value->attribute = concat_strings( attribute, ":", NULL );
    new_value->string = new_string( "...", NULL );
    free_mem( &attribute );
  } 
  else 
  { 
    attribute = parse_symbol();
    parse_token( ':' );
    new_value = parse_value();
    new_value->attribute = concat_strings( attribute, ":", NULL );
    free_mem( &attribute );
  }
  return new_value;
}

/*---------------------------------------------------------------------------*/

static gvalue_t *
parse_value( void )
/* Parse a value and return it as a "gvalue_t". */
{
  gvalue_t *new_value;
  gvalue_t *element; /* Last list or record element. */
  
  new_value = new_mem( sizeof( gvalue_t ) );
  switch (next_token) 
  {
  case '<':
    new_value->value_type = GLIST_TYPE;
    read_next_token();
    if (next_token != '>') 
    { 
      /* Insert NEW_VALUE as first list element. */
      new_value->first = parse_value();
      element = new_value->first;
      while (next_token == ',') 
      { 
        /* Insert NEW_VALUE as successor element. */
      read_next_token();
        element->next = parse_value();
        element = element->next;
      }
    }
    parse_token( '>' );
    break;
  case '[':
    new_value->value_type = GRECORD_TYPE;
    read_next_token();
    if (next_token != ']') 
    { 
      new_value->first = parse_attribute_value_pair();
      element = new_value->first;
      while (next_token == ',') 
      { 
      read_next_token();
        element->next = parse_attribute_value_pair();
        element = element->next;
      }
    }
    parse_token( ']' );
    break;
  case TOK_IDENT:
    new_value->value_type = GSTRING_TYPE;
    new_value->string = parse_symbol();
    break;
  case TOK_STRING:
    new_value->value_type = GSTRING_TYPE;
    new_value->string = new_string_readable( token_string, NULL );
    read_next_token();
    break;
  case TOK_NUMBER:
    new_value->value_type = GSTRING_TYPE;
    new_value->string = double_to_string( token_number );
    read_next_token();
    break;
  case '-':
    read_next_token();
    test_token( TOK_NUMBER );
    new_value->value_type = GSTRING_TYPE;
    new_value->string = double_to_string( -token_number );
    read_next_token();
    break;
  default:
    complain( "Value expected, not \"%s\".", token_as_text( next_token ) );
  }
  return new_value;
}

/* Displaying Malaga values. ================================================*/

int_t
get_space_width( canvas_t *canvas )
/* Get space width in pixels for CANVAS. */
{
  return canvas->space_width;
}

/*---------------------------------------------------------------------------*/

int_t
get_font_height( canvas_t *canvas )
/* Get font height in pixels for CANVAS. */
{
  return canvas->font_height;
}

/*---------------------------------------------------------------------------*/

int_t
get_font_ascent( canvas_t *canvas )
/* Get number of pixels above baseline of current font for CANVAS. */
{
  return canvas->font_ascent;
}

/*---------------------------------------------------------------------------*/

int_t 
get_border_width( canvas_t *canvas )
/* Get border width of CANVAS in pixels. */
{
  return (ps_mode ? 500 : 5);
}

/*---------------------------------------------------------------------------*/

void 
set_color( color_t color )
/* Set the current drawing color to COLOR. */
{
  if (ps_mode)
  {
    if (color == WHITE) 
      fprintf( ps_stream, "1 G\n" );
    else 
      fprintf( ps_stream, "0 G\n" );
  }
  else 
    gdk_gc_set_foreground( gc, &colors[ color ] );
}

/*---------------------------------------------------------------------------*/

void 
draw_lines( int_t count, int_t x, int_t y, ... )
/* Called as "draw_lines( COUNT, x_1, y_1, x_2, y_2, ..., x_COUNT, y_COUNT )".
 * Draw lines:
 * from (x_1, y_1) to (x_2, y_2),
 * from (x_2, y_2) to (x_3, y_3),
 * ...
 * from (x_COUNT-1, y_COUNT-1) to (x_COUNT, y_COUNT). */
{
  va_list args;
  int_t i;
  static GdkPoint *p;
  static int_t max_points;
  bool_t left, right, over, under;

  if (ps_mode)
  {
    fprintf( ps_stream, "%d %d M ", x, area.height - y );
    va_start( args, y );
    for (i = 1; i < count; i++)
    {
      x = va_arg( args, int_t );
      y = va_arg( args, int_t );
      fprintf( ps_stream, "%d %d L ", x, area.height - y );
    }
    va_end( args );
    fprintf( ps_stream, "S\n" );
  }
  else
  {
    if (count > max_points) 
      max_points = renew_vector( &p, sizeof( GdkPoint ), count );
    left = right = over = under = FALSE;
    va_start( args, y );
    for (i = 0; ; i++)
    {
      x -= area.x;
      y -= area.y;
      if (x >= 0) 
      right = TRUE;
      if (x < area.width) 
      left = TRUE;
      if (y >= 0) 
      under = TRUE;
      if (y < area.height) 
      over = TRUE;
      p[i].x = x; 
      p[i].y = y;
      if (i >= count - 1) 
      break;
      x = va_arg( args, int_t );
      y = va_arg( args, int_t );
    }
    va_end( args );
    if (left && right && over && under) 
      gdk_draw_lines( pixmap, gc, p, count );
  }
}

/*---------------------------------------------------------------------------*/

void 
draw_rectangle( int_t x, int_t y, int_t width, int_t height )
{
  if (ps_mode)
  { 
    fprintf( ps_stream, "%d %d M ", x, area.height - y );
    fprintf( ps_stream, "%d %d R ", width, 0 );
    fprintf( ps_stream, "%d %d R ", 0, -height );
    fprintf( ps_stream, "%d %d R ", -width, 0 );
    fprintf( ps_stream, "F\n" );
  }
  else
  {
    x -= area.x;
    y -= area.y;
    if (x + width > 0 && x < area.width && y + height > 0 && y < area.height)
      gdk_draw_rectangle( pixmap, gc, TRUE, x, y, width, height );
  }
}

/*---------------------------------------------------------------------------*/

void 
draw_circle( bool_t filled, int_t x, int_t y, int_t r )
{
  if (ps_mode)
  {
    if (filled) 
      fprintf( ps_stream, "%d %d %d D\n", x, area.height - y, r );
    else 
      fprintf( ps_stream, "%d %d %d C\n", x, area.height - y, r );
  }
  else
  {
    x -= area.x;
    y -= area.y;
    if (x + r >= 0 && x - r <= area.width 
      && y + r >= 0 && y - r <= area.height)
    {
      gdk_draw_arc( pixmap, gc, filled, x - r, y - r, 2 * r, 2 * r, 
                0, 64 * 360 );
    }
  }
}

/*---------------------------------------------------------------------------*/

#ifdef UNIX
static int_t
string_to_wc_string( string_t string )
/* Convert KSC STRING to string in X11 wide char format.
 * Return its length. The converted string is in HANGUL_STRING. */
{
  int_t n;
  
  for (n = 0; ORD( string[n] ) >= 0xa0 && ORD( string[n + 1] ) >= 0xa0; n += 2)
  { 
    if (n + 2 > BUFFER_SIZE) 
      break;

    /* Convert KSC5601 character. */
    hangul_string[n] = ORD( string[n] ) & 0x7f;
    hangul_string[n + 1] = ORD( string[n + 1] ) & 0x7f;
  }
  return n;
}
#endif

/*---------------------------------------------------------------------------*/

static string_t
string_to_hangul_ps_string( string_t *string )
/* Convert *STRING (in KSC coding) to Hangul PostScript string.
 * Return its length. The converted string is in HANGUL_STRING. */
{
  string_t s;
  int_t i, trigem, c;
  
  i = 0;
  for (s = *string; *s != EOS && i + 4 < BUFFER_SIZE; )
  {
    if (ORD( s[0] ) >= 0x80)
    {
      trigem 
      = ksc_table[ ksc_to_table_index( ORD( s[0] ) << 8 | ORD( s[1] ) ) ];
      s += 2;
      c = INITIAL_CONSONANT( trigem ); 
      if (c != 0) 
      hangul_string[ i++ ] = c;
      c = VOWEL( trigem );
      if (c != 0) 
      hangul_string[ i++ ] = c;
      c = FINAL_CONSONANT( trigem );
      if (c != 0) 
      hangul_string[ i++ ] = c;
    }
    else 
      hangul_string[ i++ ] = *s++;
  }
  hangul_string[i] = EOS;
  *string = s;
  return hangul_string;
}

/*---------------------------------------------------------------------------*/

static int_t
ps_string_width( string_t string_start )
{
  string_t s;
  int_t width;

  width = 0;
  for (s = string_start; *s != EOS; s++)
    width += (hangul) ? widths_hangul[ ORD(*s) ] : widths_iso88591[ ORD(*s) ];
  return width;
}

/*---------------------------------------------------------------------------*/

static int_t
string_width( canvas_t *canvas, string_t string )
/* Return width of STRING in pixels when drawn in CANVAS. */
{
  int_t n, width;

  if (ps_mode)
  {
    if (! hangul) 
      return ps_string_width( string );

    /* Hangul KSC5601 section - measure hangul characters in Hangul font.
     * All other characters are measured in normal font. */
    width = 0;
    while (*string != EOS)
      width += ps_string_width( string_to_hangul_ps_string( &string ) );
    return width;
  }

#ifdef UNIX
  if (! hangul) 
    return gdk_string_width( canvas->font, string );

  /* Hangul KSC5601 section - measure hangul characters in Hangul font. 
   * All other characters are measured in normal font. */
  width = 0;
  while (*string != EOS) 
  { 
    /* Add width of a Hangul segment. */
    n = string_to_wc_string( string );
    if (n > 0) 
      width += gdk_text_width( canvas->hangul_font, hangul_string, n );
    string += n;

    /* Add width of an ASCII segment. */
    for (n = 0; ORD( string[n] ) < 0xa0 || ORD( string[n + 1] ) < 0xa0; n++) 
    { 
      if (string[n] == EOS) 
      break;
    }
    if (n > 0) 
      width += gdk_text_width( canvas->font, string, n );
    string += n;
  }
  return width;
#endif

#ifdef WINDOWS
  width = 0;
  while (*string != EOS)
  {
    string_t utf8;
    int_t len;

    if (hangul)
      utf8 = g_convert( string, -1, "utf-8", "euc-kr", &n, &len, NULL );
    else 
      utf8 = g_locale_to_utf8( string, -1, &n, &len, NULL );
    string += n;
    width += gdk_text_width( canvas->font, utf8, len );
    g_free( utf8 );
  }
  return width;
#endif
}

/*---------------------------------------------------------------------------*/

static void
print_ps_text( string_t string )
/* Convert STRING to Postscript format: prefix "(", ")", and "\" with "\",
 * insert line breaks if line gets too long. */
{
  int_t col;
  string_t s;

  while (*string != EOS)
  {
    /* Consume as much as possible of STRING and convert it to PS coding. */
    if (hangul) 
      s = string_to_hangul_ps_string( &string );
    else
    {
      s = string;
      string += strlen( string );
    }

    /* Put that section into the PS stream. */
    while (*s != EOS)
    {
      if (hangul && ORD( *s ) >= 0x80)
      {
      fprintf( ps_stream, "<" );
      col = 1;
      for (; ORD( *s ) >= 0x80; s++)
      {
        if (col > 76) /* Insert line break if line gets too long. */
        {
          fprintf( ps_stream, "\n" );
          col = 0;
        }
        fprintf( ps_stream, "%02x", ORD( *s ) );
        col += 2;
      }
      fprintf( ps_stream, ">\nH\n" );
      }
      else
      {
      fprintf( ps_stream, "(" );
      col = 1;
      for (; *s != EOS && (! hangul || ORD( *s ) < 0x80); s++)
        {
        if (col > 76) /* Insert line break if line gets too long. */
          {
          fprintf( ps_stream, "\\\n" );
          col = 0;
          }
        if (*s == '(' || *s == ')' || *s == '\\')
          {
          fputc( '\\', ps_stream );
          col++;
        }
        fputc( *s, ps_stream );
        col++;
      }
      fprintf( ps_stream, ")\nT\n" );
      }
    }
  }
}

/*---------------------------------------------------------------------------*/

static void
draw_string( canvas_t *canvas, string_t string, 
           int_t x, int_t y, int_t width )
/* Draw STRING, which is WIDTH pixels wide, at position (X,Y) in CANVAS. */
{
  int_t n;

  if (ps_mode)
  {
    fprintf( ps_stream, "%d %d M\n", 
           x, area.height - y - canvas->font_ascent );
    print_ps_text( string );
    return;
  }

  x -= area.x;
  y -= area.y;
  if (x + width <= 0 || x >= area.width) 
    return;
  if (y + canvas->font_height <= 0 || y >= area.height) 
    return;
  
  y += canvas->font_ascent;

#ifdef UNIX
  if (! hangul) 
    gdk_draw_string( pixmap, canvas->font, gc, x, y, string );
  else
  {
    /* Hangul KSC5601 section - show hangul characters in Hangul font. 
     * All other characters are shown in normal font. */
    while (*string != EOS) 
    { 
      /* Draw Hangul segment. */
      n = string_to_wc_string( string );
      if (n > 0) 
      { 
      gdk_draw_text( pixmap, canvas->hangul_font, gc, x, y, hangul_string, 
                   n );
      x += gdk_text_width( canvas->hangul_font, hangul_string, n );
      string += n;
      }

      /* Draw ASCII segment. */
      for (n = 0; ORD( string[n] ) < 0xa0 || ORD( string[n + 1] ) < 0xa0; n++) 
      { 
      if (string[n] == EOS) 
        break;
      }
      if (n > 0) 
      { 
      gdk_draw_text( pixmap, canvas->font, gc, x, y, string, n );
      x += gdk_text_width( canvas->font, string, n );
      string += n;
      }
    }
  }  
#endif

#ifdef WINDOWS
  while (*string != EOS)
  {
    string_t utf8;
    int_t len;

    if (hangul)
      utf8 = g_convert( string, -1, "utf-8", "euc-kr", &n, &len, NULL );
    else 
      utf8 = g_locale_to_utf8( string, -1, &n, &len, NULL );
    string += n;
    gdk_draw_text( pixmap, canvas->font, gc, x, y, utf8, len );
    if (*string != EOS) 
      x += gdk_text_width( canvas->font, utf8, len );
    g_free( utf8 );
  }
#endif
}

/*---------------------------------------------------------------------------*/

static int_t
max_attribute_width( gvalue_t *value ) 
/* Compute maximum attribute width of record VALUE. */
{
  gvalue_t *element;
  int_t max_length;

  max_length = 0;
  for (element = value->first; element != NULL; element = element->next) 
    max_length = MAX( max_length, element->attribute_width );
  return max_length;
}

/*---------------------------------------------------------------------------*/

static void
config_value_size( canvas_t *canvas, gvalue_t *value )
/* Compute the height and width of VALUE in CANVAS. */
{
  gvalue_t *element;
  int_t max_attrib_width, max_width, descent;
    
  if (value->attribute != NULL)
    value->attribute_width = string_width( canvas, value->attribute );
  switch (value->value_type) 
  {
  case GRECORD_TYPE:
    value->height = 0;
    max_attrib_width = 0;
    max_width = 0;
    value->ascent = canvas->border_height + canvas->font_ascent; /* Default. */
    for (element = value->first; element != NULL; element = element->next) 
    { 
      config_value_size( canvas, element );
      max_attrib_width = MAX( max_attrib_width, element->attribute_width );
      max_width = MAX( max_width, element->width );
      if (! canvas->hanging) 
      value->ascent= canvas->border_height + value->height + element->ascent;
      value->height += element->height;
    }
    if (canvas->hanging && value->first != NULL)
      value->ascent = canvas->border_height + value->first->ascent;
    value->height = (canvas->border_height
                 + MAX( value->height, canvas->font_height )
                 + canvas->border_height);
    value->width = (canvas->border_width + max_attrib_width 
                + canvas->space_width + max_width
                + canvas->border_width);
    break;
  case GLIST_TYPE:
    value->width = 0;
    descent = canvas->font_height - canvas->font_ascent;
    value->ascent = canvas->font_ascent;
    for (element = value->first; element != NULL; element = element->next) 
    { 
      config_value_size( canvas, element );
      descent = MAX( descent, element->height - element->ascent );
      value->ascent = MAX( value->ascent, element->ascent );
      value->width += element->width;
      if (element->next != NULL) 
      value->width += (canvas->comma_width + canvas->space_width);
    }
    if (value->width == 0) 
      value->width = 2;
    value->ascent += canvas->border_height;
    value->height = value->ascent + descent + canvas->border_height; 
    value->width += 2 * (canvas->border_width / 3 
                   + ((value->height - canvas->border_height) 
                      / BRACKET_RATIO));
    break;
  case GSTRING_TYPE:
    value->width = string_width( canvas, value->string );
    value->height = canvas->font_height;
    value->ascent = canvas->font_ascent;
    break;
  }
}

/*---------------------------------------------------------------------------*/

static void 
draw_value( canvas_t *canvas, gvalue_t *value, int_t x, int_t y )
/* Draw VALUE in CANVAS at X/Y. */
{
  gvalue_t *element;
  int_t x2, bracket_width;

  /* If (sub)value is out of bounds, we don't need to draw it. */
  if (x >= area.x + area.width || y >= area.y + area.height
      || x + value->width <= area.x || y + value->height <= area.y) 
  { 
    return; 
  }

  switch (value->value_type) 
  {
  case GRECORD_TYPE:

    /* Draw left bracket. */
    draw_lines( 4,
            x + canvas->border_width * 5 / 6,
            y + canvas->border_height / 2,
            x + canvas->border_width / 6,
            y + canvas->border_height / 2,
            x + canvas->border_width / 6,
            y + value->height - 1 - canvas->border_height / 2,
            x + canvas->border_width * 5 / 6,
            y + value->height - 1 - canvas->border_height / 2 );

    /* Draw right bracket. */
    draw_lines( 4,
            x + value->width - 1 - canvas->border_width * 5 / 6, 
            y + canvas->border_height / 2, 
            x + value->width - 1 - canvas->border_width / 6, 
            y + canvas->border_height / 2,
            x + value->width - 1 - canvas->border_width / 6, 
            y + value->height - 1 - canvas->border_height / 2,
            x + value->width - 1 - canvas->border_width * 5 / 6, 
            y + value->height - 1 - canvas->border_height / 2 );

    /* Draw elements. */
    x2 = (x + canvas->border_width + 
        max_attribute_width( value ) + canvas->space_width);
    y += canvas->border_height;
    for (element = value->first; element != NULL; element = element->next) 
    { 
      /* Draw attribute name. */
      draw_string( canvas, element->attribute, 
               x + canvas->border_width, 
               y + (element->height - canvas->font_height) / 2,
               element->attribute_width );

      /* Draw attribute value. */
      draw_value( canvas, element, x2, y );
      y += element->height;
    }
    break;
  case GLIST_TYPE:
    bracket_width = (canvas->border_width / 3
                 + ((value->height - canvas->border_height) 
                  / BRACKET_RATIO));

    /* Draw left bracket. */
    draw_lines( 3,
            x + bracket_width - canvas->border_width / 6, 
            y + canvas->border_height / 2, 
            x + canvas->border_width / 6, 
            y + value->height / 2,
            x + bracket_width - canvas->border_width / 6, 
            y + value->height - 1 - canvas->border_height / 2 );

    /* Draw right bracket. */
    draw_lines( 3,
            x + value->width - 1
            - (bracket_width - canvas->border_width / 6),
            y + canvas->border_height / 2, 
            x + value->width - 1 - canvas->border_width / 6,
            y + value->height / 2,
            x + value->width - 1 
            - (bracket_width - canvas->border_width / 6),
            y + value->height - 1 - canvas->border_height / 2 );

    /* Draw elements. */
    x += bracket_width;
    y += value->ascent; /* Baseline. */
    for (element = value->first; element != NULL; element = element->next) 
    { 
      draw_value( canvas, element, x, y - element->ascent );
      x += element->width;
      if (element->next != NULL) /* Draw ",". */
      { 
      draw_string( canvas, ",", x, y - canvas->font_ascent,
                 canvas->comma_width );
        x += (canvas->comma_width + canvas->space_width);
      }
    }
    break;
  case GSTRING_TYPE:
    draw_string( canvas, value->string, x, y, value->width );
    break;
  }
}

/*---------------------------------------------------------------------------*/

static gboolean 
expose_event( GtkWidget *widget, GdkEventExpose *event, canvas_t *canvas ) 
{
  GdkRectangle clip;

  area.x = canvas->x + event->area.x;
  area.y = canvas->y + event->area.y;
  clip.x = clip.y = 0;
  clip.width = area.width = event->area.width;
  clip.height = area.height = event->area.height;

  /* Make sure PIXMAP is large enough for buffering AREA. */
  if (pixmap == NULL 
      || pixmap_width < area.width || pixmap_height < area.height)
  {
    if (pixmap != NULL) 
      gdk_pixmap_unref( pixmap );
    pixmap_width = MAX( pixmap_width, area.width );
    pixmap_height = MAX( pixmap_height, area.height );
    pixmap = gdk_pixmap_new( canvas->draw_area->window, 
                       pixmap_width, pixmap_height, -1 );
    if (pixmap == NULL) 
      complain( "Out of display memory." );
  }

  /* Draw into PIXMAP and copy the result to the draw area window.  */
  gdk_gc_set_clip_rectangle( gc, &clip );
  gdk_gc_set_foreground( gc, &canvas->window->style->bg[ GTK_STATE_NORMAL ] );
  gdk_draw_rectangle( pixmap, gc, TRUE, 0, 0, area.width, area.height );
  canvas->expose( canvas, &area );
  gdk_gc_set_clip_rectangle( gc, NULL );
  gdk_draw_pixmap( canvas->draw_area->window, gc, pixmap,
               0, 0, event->area.x, event->area.y, 
               area.width, area.height );

  return TRUE;
}

/*---------------------------------------------------------------------------*/

static void
configure_draw_area( canvas_t *canvas )
{
  int_t ascent, descent;

  if (ps_mode)
  {
    canvas->border_width = 600;
    canvas->border_height = 300;
    canvas->space_width = ps_string_width( " " );
    canvas->comma_width = ps_string_width( "," );
    canvas->font_ascent = 917;
    canvas->font_height = 1100;
  }
  else
  {
    canvas->border_width = 6;
    canvas->border_height = 3;
    canvas->space_width = gdk_char_width( canvas->font, ' ' );
    canvas->comma_width = gdk_char_width( canvas->font, ',' );
    ascent = canvas->font->ascent;
    descent = canvas->font->descent + 1;
#ifdef UNIX
    if (hangul)
    {
      ascent = MAX( ascent, canvas->hangul_font->ascent );
      descent = MAX( descent, canvas->hangul_font->descent + 1 );
    }
#endif
    canvas->font_ascent = ascent;
    canvas->font_height = ascent + descent;
  }
  canvas->configure( canvas, &canvas->width, &canvas->height );
}

/*---------------------------------------------------------------------------*/

static void
write_postscript( canvas_t *canvas )
{
  double scaling;
  time_t now;

  area.x = 0;
  area.y = 0;
  area.width = canvas->width;
  area.height = canvas->height;

  scaling = 0.01;
  if (area.width * scaling > PAGE_WIDTH) 
    scaling = PAGE_WIDTH / (double) area.width;
  if (area.height * scaling > PAGE_HEIGHT) 
    scaling = PAGE_HEIGHT / (double) area.height;
  
  fprintf( ps_stream, 
         "%%!PS-Adobe-3.0 EPSF-3.0\n"
         "%%%%BoundingBox: %d %d %d %d\n",
         (int_t) PAPER_BORDER, (int_t) PAPER_BORDER,
         (int_t) ceil( area.width * scaling + PAPER_BORDER ), 
         (int_t) ceil( area.height * scaling + PAPER_BORDER ) );
         
  fprintf( ps_stream, 
         "%%%%HiResBoundingBox: %f %f %f %f\n", 
         PAPER_BORDER, PAPER_BORDER,
         area.width * scaling + PAPER_BORDER, 
         area.height * scaling + PAPER_BORDER );

  fprintf( ps_stream, 
         "%%%%Creator: malshow\n"
         "%%%%Title: %s\n", GTK_WINDOW( canvas->window )->title );

  time( &now );
  fprintf( ps_stream, "%%%%CreationDate: %s", ctime( &now ) );

  fprintf( ps_stream, 
         "%%%%DocumentData: Clean8Bit\n"
         "%%%%Orientation: Portrait\n" );

  if (hangul) 
    fprintf( ps_stream, "%%%%DocumentSuppliedResources: font n3f-5\n" );

  fprintf( ps_stream,
         "%%%%DocumentNeededResources: font Helvetica\n"
         "%%%%EndComments\n" );

  if (hangul) 
    fprintf( ps_stream, "%s", hangul_ps_prolog );

  fprintf( ps_stream, "save 11 dict begin\n" );

  if (! hangul)
  {
  /* Create ISOLatin1Encoding for Postscript Level 1 compatibility. */
  fprintf( ps_stream, 
         "/ISOLatin1Encoding where {pop} {/ISOLatin1Encoding [\n"
         "/space /space /space /space /space /space /space /space /space\n"
         "/space /space /space /space /space /space /space /space /space\n"
         "/space /space /space /space /space /space /space /space /space\n"
         "/space /space /space /space /space /space /exclam /quotedbl\n"
         "/numbersign /dollar /percent /ampersand /quoteright /parenleft\n"
         "/parenright /asterisk /plus /comma /minus /period /slash /zero\n"
         "/one /two /three /four /five /six /seven /eight /nine /colon\n"
         "/semicolon /less /equal /greater /question /at /A /B /C /D /E /F\n"
         "/G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z\n"
         "/bracketleft /backslash /bracketright /asciicircum /underscore\n"
         "/quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r\n"
         "/s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde\n"
         "/space /space /space /space /space /space /space /space /space\n"
         "/space /space /space /space /space /space /space /space\n"
         "/dotlessi /grave /acute /circumflex /tilde /macron /breve\n"
         "/dotaccent /dieresis /space /ring /cedilla /space /hungarumlaut\n"
         "/ogonek /caron /space /exclamdown /cent /sterling /currency /yen\n"
         "/brokenbar /section /dieresis /copyright /ordfeminine\n"
         "/guillemotleft /logicalnot /hyphen /registered /macron /degree\n"
         "/plusminus /twosuperior /threesuperior /acute /mu /paragraph\n"
         "/periodcentered /cedilla /onesuperior /ordmasculine\n"
         "/guillemotright /onequarter /onehalf /threequarters\n"
         "/questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis\n"
         "/Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis\n"
         "/Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve\n"
         "/Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n"
         "/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn\n"
         "/germandbls /agrave /aacute /acircumflex /atilde /adieresis\n"
         "/aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis\n"
         "/igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve\n"
         "/oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave\n"
         "/uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n"
         "] def} ifelse\n" );
  }

  fprintf( ps_stream, 
         "/C {0 360 arc stroke} bind def\n" /* Paint a circle. */
         "/D {0 360 arc fill} bind def\n" /* Paint a filled disc. */
         "/F {closepath fill} bind def\n"
         "/G /setgray load def\n"
         "/L /lineto load def\n"
         "/M /moveto load def\n"
         "/R /rlineto load def\n"
         "/S /stroke load def\n" );
  
  if (hangul) 
  {
    fprintf( ps_stream, 
           "/T { /Helvetica 1000 selectfont show } bind def\n"
           "/H { /n3f-5 1000 selectfont show } bind def\n" );
  }
  else
  {
    /* Define new font Helvetica-ISOLatin1 with ISOLatin1Encoding */
    fprintf( ps_stream, 
           "/T /show load def\n"
           "/Helvetica-ISOLatin1\n"
           "/Helvetica findfont\n" 
           "dup length dict copy\n"
           "dup /FID undef\n"
           "dup /Encoding ISOLatin1Encoding put\n"
           "definefont 1000 scalefont setfont\n" );
  }

  fprintf( ps_stream, "25 setlinewidth\n" );
  fprintf( ps_stream, "%f %f translate\n", PAPER_BORDER, PAPER_BORDER );
  fprintf( ps_stream, "%f %f scale\n", scaling, scaling );

  canvas->expose( canvas, &area );
  fprintf( ps_stream, 
         "end restore showpage\n"
         "%%%%EOF\n" );
}

/*---------------------------------------------------------------------------*/

static void
save_postscript( GtkWidget *widget, canvas_t *canvas )
{
  GtkWidget *dialog, *label, *okay_button;  
  string_t file_name;

  file_name = gtk_file_selection_get_filename(
    GTK_FILE_SELECTION( canvas->file_selection ) );
  TRY
  {
    ps_stream = open_stream( file_name, "w" );
    ps_mode = TRUE;
    configure_draw_area( canvas );
    write_postscript( canvas );
    if (ferror( ps_stream )) 
      complain( "Can't write to \"%s\": %s.", strerror( errno ) );
    close_stream( &ps_stream, file_name );
    ps_mode = FALSE;
    configure_draw_area( canvas );   
  }
  IF_ERROR 
  { 
    /* Reconfigure canvas before doing any GTK stuff, so it can be safely
     * exposed. */
    close_stream( &ps_stream, NULL );
    ps_mode = FALSE;
    configure_draw_area( canvas );   

    /* Show the error message. */
    dialog = gtk_dialog_new();
    gtk_window_set_title( GTK_WINDOW( dialog ), "Export Postscript" );
    gtk_window_set_transient_for( GTK_WINDOW( dialog ),
                          GTK_WINDOW( canvas->window ) );
    gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
    label = gtk_label_new( error_text->buffer );
    gtk_misc_set_padding( GTK_MISC( label ), 10, 10 );
    okay_button = gtk_button_new_with_label( "OK" );
    gtk_signal_connect_object( GTK_OBJECT( okay_button ), "clicked",
                               GTK_SIGNAL_FUNC( gtk_widget_destroy ), 
                         GTK_OBJECT( dialog ) );
    gtk_container_add( GTK_CONTAINER( GTK_DIALOG( dialog )->action_area ),
                       okay_button );
    gtk_container_add( GTK_CONTAINER( GTK_DIALOG( dialog )->vbox ), label);
    gtk_widget_show_all( dialog );
    RESUME;
  }
  END_TRY;
  gtk_widget_hide( canvas->file_selection );
  canvas->file_selection = NULL;
}

/*---------------------------------------------------------------------------*/

static void
export_postscript( canvas_t *canvas )
/* Open "Export Postscript" selector */
{
  string_t title;

  if (canvas->file_selection == NULL)
  {
    title = concat_strings( "Export ", GTK_WINDOW( canvas->window )->title,
                      " as Postscript", NULL );
    canvas->file_selection = gtk_file_selection_new( title );
    free_mem( &title );
    gtk_window_set_transient_for( GTK_WINDOW( canvas->file_selection ),
                          GTK_WINDOW( canvas->window ) );
    gtk_window_set_position( GTK_WINDOW( canvas->file_selection ), 
                       GTK_WIN_POS_MOUSE );
    gtk_file_selection_hide_fileop_buttons(
      GTK_FILE_SELECTION( canvas->file_selection ) );
    gtk_file_selection_set_filename(
      GTK_FILE_SELECTION( canvas->file_selection ), canvas->ps_file_name );
    gtk_signal_connect( 
      GTK_OBJECT( GTK_FILE_SELECTION( canvas->file_selection )->ok_button ),
       "clicked", save_postscript, canvas ); 
    gtk_signal_connect_object(
      GTK_OBJECT( 
      GTK_FILE_SELECTION( canvas->file_selection )->cancel_button ),
       "clicked", gtk_widget_hide, GTK_OBJECT( canvas->file_selection ) );
    gtk_signal_connect( GTK_OBJECT( canvas->file_selection ), 
                  "delete_event", gtk_widget_hide, NULL );
  }
  gtk_widget_show( canvas->file_selection );
}

/*---------------------------------------------------------------------------*/

static void
adjust_canvas( canvas_t *canvas )
{
  if (canvas->x + canvas->area_width > canvas->width)
    canvas->x = canvas->width - canvas->area_width;
  if (canvas->x < 0) 
    canvas->x = 0;

  if (canvas->y + canvas->area_height > canvas->height)
    canvas->y = canvas->height - canvas->area_height;
  if (canvas->y < 0) 
    canvas->y = 0;

  canvas->hadjust->value = canvas->x; 
  canvas->hadjust->upper = canvas->width;
  canvas->hadjust->page_increment = MAX( 20, canvas->area_width - 20 );
  canvas->hadjust->page_size = canvas->area_width;

  canvas->vadjust->value = canvas->y;
  canvas->vadjust->upper = canvas->height;
  canvas->vadjust->page_increment = MAX( 20, canvas->area_height - 20 );
  canvas->vadjust->page_size = canvas->area_height;

#if GTK_CHECK_VERSION( 1, 3, 0 )
  gtk_adjustment_changed( canvas->hadjust );
  gtk_adjustment_changed( canvas->vadjust );
#else
  {
    GtkRange *hrange = GTK_RANGE( canvas->hscrollbar );
    GtkRange *vrange = GTK_RANGE( canvas->vscrollbar );

    /* We can't use "gtk_adjustment_changed" here because it's buggy,
     * so we have to use a hack: */
    hrange->old_value = canvas->hadjust->value;
    hrange->old_upper = canvas->hadjust->upper;
    hrange->old_page_size = canvas->hadjust->page_size;
    gtk_range_slider_update( hrange );
    vrange->old_value = canvas->vadjust->value;
    vrange->old_upper = canvas->vadjust->upper;
    vrange->old_page_size = canvas->vadjust->page_size;
    gtk_range_slider_update( vrange );
  }
#endif

  /* If the pointer is currently in the drawing area, we might update the
   * cursor. */
  if (canvas->mouse_event != NULL)
  {
    int x, y;
    GdkModifierType mask;

    gdk_window_get_pointer( canvas->draw_area->window, &x, &y, &mask );
    canvas->mouse_event( canvas, canvas->x + x, canvas->y + y, 0 );
  }
}

/*---------------------------------------------------------------------------*/

static gboolean
delete_event( GtkWidget *widget, GdkEvent *event, canvas_t *canvas )
{
  hide_canvas( canvas );
  return TRUE;
}

/*---------------------------------------------------------------------------*/

static gboolean
key_press_event( GtkWidget *widget, GdkEventKey *event, canvas_t *canvas )
{
  switch (event->keyval)
  {
  case GDK_Left:
    if ((event->state & GDK_CONTROL_MASK) != 0) 
      canvas->x = 0;
    else if ((event->state & GDK_SHIFT_MASK) != 0)
      canvas->x -= canvas->hadjust->page_increment;
    else 
      canvas->x -= 10; 
    break;
  case GDK_Right:
    if ((event->state & GDK_CONTROL_MASK) != 0) 
      canvas->x = canvas->width;
    else if ((event->state & GDK_SHIFT_MASK) != 0)
      canvas->x += canvas->hadjust->page_increment;
    else 
      canvas->x += 10; 
    break;
  case GDK_Up: 
    if ((event->state & GDK_CONTROL_MASK) != 0) 
      canvas->y = 0;
    else if ((event->state & GDK_SHIFT_MASK) != 0)
      canvas->y -= canvas->vadjust->page_increment; 
    else 
      canvas->y -= 10; 
    break;
  case GDK_Down: 
    if ((event->state & GDK_CONTROL_MASK) != 0) 
      canvas->y = canvas->height;
    else if ((event->state & GDK_SHIFT_MASK) != 0)
      canvas->y += canvas->vadjust->page_increment; 
    else 
      canvas->y += 10; 
    break;
  case GDK_Home: 
    if ((event->state & GDK_CONTROL_MASK) != 0)
      canvas->x -= canvas->hadjust->page_increment;
    else 
      canvas->x = 0; 
    break;
  case GDK_End:
    if ((event->state & GDK_CONTROL_MASK) != 0)
      canvas->x += canvas->hadjust->page_increment;
    else 
      canvas->x = canvas->width; 
    break;
  case GDK_Page_Up:
    if ((event->state & GDK_CONTROL_MASK) != 0) 
      canvas->y = 0;
    else 
      canvas->y -= canvas->vadjust->page_increment; 
    break;
  case GDK_Page_Down: 
    if ((event->state & GDK_CONTROL_MASK) != 0) 
      canvas->y = canvas->height;
    else 
      canvas->y += canvas->vadjust->page_increment; 
    break;
  default:
    return FALSE;
  }
  adjust_canvas( canvas );
  gtk_widget_queue_draw( canvas->draw_area );
  return TRUE;
}

/*---------------------------------------------------------------------------*/

static void 
set_font_size( canvas_t *canvas, guint font_size )
{
  canvas->font_size = font_size;
  configure_canvas( canvas );
}

/*---------------------------------------------------------------------------*/

static void 
set_hanging( canvas_t *canvas, guint action, GtkWidget *item )
{
  canvas->hanging = GTK_CHECK_MENU_ITEM( item )->active;
  configure_canvas( canvas );
}

/*---------------------------------------------------------------------------*/

static void 
configure_event( GtkWidget *widget, 
             GdkEventConfigure *event, 
             canvas_t *canvas )
/* Called if CANVAS window size has changed. */
{
  canvas->area_width = event->width;
  canvas->area_height = event->height;
  adjust_canvas( canvas );
}

/*---------------------------------------------------------------------------*/

static void 
adjust_value_changed( GtkAdjustment *adjust, canvas_t *canvas )
/* Called if ADJUST has changed its value. Draw CANVAS at the new position. */
{
  canvas->x = canvas->hadjust->value;
  canvas->y = canvas->vadjust->value;
  gtk_widget_queue_draw( canvas->draw_area );
}

/*---------------------------------------------------------------------------*/

void
make_visible( canvas_t *canvas, int_t x, int_t y )
/* Make sure the point (X,Y) is in the displayed part of CANVAS. */
{
  if (x < canvas->x || x > canvas->x + canvas->area_width)
    canvas->x =  x - canvas->area_width / 2;
  if (y < canvas->y || y > canvas->y + canvas->area_height)
    canvas->y = y - canvas->area_height / 2;
  adjust_canvas( canvas );
}

/*---------------------------------------------------------------------------*/

void
go_canvas_bottom( canvas_t *canvas )
/* Go to the bottom left of CANVAS. */
{
  canvas->x = 0;
  if (canvas->height < canvas->area_height) 
    canvas->y = 0;
  else 
    canvas->y = canvas->height - canvas->area_height;
  adjust_canvas( canvas );
}

/*---------------------------------------------------------------------------*/

void
configure_canvas( canvas_t *canvas )
/* Call this if CANVAS must be reconfigured because its content has changed
 * its size (font size change, new content, etc.). */
{
  text_t *font_descriptor;
  GdkFont *new_font;

  font_descriptor = new_text();
#ifdef UNIX
  print_text( font_descriptor, "-*-%s-medium-r-normal-*-%d-*-*-*-*-*-%s", 
            font_name, canvas->font_size, char_set );
#endif
#ifdef WINDOWS
  print_text( font_descriptor, "-*-%s-*-r-normal-*-*-%d-*-*-*-*-%s", 
            font_name, canvas->font_size * 10, char_set );
#endif
  new_font = gdk_font_load( font_descriptor->buffer );
  if (new_font == NULL) 
    complain( "No font \"%s\".", font_descriptor->buffer );
  free_text( &font_descriptor );
  if (canvas->font != NULL) 
    gdk_font_unref( canvas->font );
  canvas->font = new_font;
#ifdef UNIX
  if (hangul && canvas->hangul_font == NULL)
  {
    canvas->hangul_font 
      = gdk_font_load( "-*-*-medium-r-normal-*-16-*-*-*-*-*-ksc5601.1987-0" );
    if (canvas->hangul_font == NULL) 
      complain( "Missing Hangul font." );
  }
#endif

  configure_draw_area( canvas );
  adjust_canvas( canvas );
  gtk_widget_queue_draw( canvas->draw_area );
}

/*---------------------------------------------------------------------------*/

void 
show_canvas( canvas_t *canvas )
{
  gdk_window_raise( canvas->window->window );
  gtk_widget_show( canvas->window );
}

/*---------------------------------------------------------------------------*/

void 
hide_canvas( canvas_t *canvas )
{
  gtk_widget_hide( canvas->window );
  canvas->file_selection = NULL;
  if (canvas->close != NULL) 
    canvas->close( canvas );
}

/*---------------------------------------------------------------------------*/

void 
redraw_canvas( canvas_t *canvas )
{
  gtk_widget_queue_draw( canvas->draw_area );
}

/*---------------------------------------------------------------------------*/

void
set_cursor( canvas_t *canvas, bool_t alternate )
/* Set cursor for CANVAS to alternate shape if ALTERNATE == TRUE. */
{
  if (canvas->alternate_cursor == alternate) 
    return;
  gdk_window_set_cursor( canvas->draw_area->window, 
                   alternate ? alternate_cursor : NULL );
  canvas->alternate_cursor = alternate;
}

/*---------------------------------------------------------------------------*/

static gboolean
mouse_event( GtkWidget *widget, GdkEventAny *event, canvas_t *canvas )
{
  if (event->type == GDK_BUTTON_PRESS) 
  {
    GdkEventButton *e = (GdkEventButton *) event;
    return canvas->mouse_event( canvas, e->x + canvas->x, e->y + canvas->y, 
                        e->button );
  }
  else if (event->type == GDK_MOTION_NOTIFY)
  {
    GdkEventMotion *e = (GdkEventMotion *) event;
    return canvas->mouse_event( canvas, 
                        e->x + canvas->x, e->y + canvas->y, 0 );
  }
  else if (event->type == GDK_ENTER_NOTIFY)
  {
    GdkEventCrossing *e = (GdkEventCrossing *) event;
    return canvas->mouse_event( canvas, 
                        e->x + canvas->x, e->y + canvas->y, 0 );
  }
  return FALSE;
}

/*---------------------------------------------------------------------------*/

/* General menu items. */
static GtkItemFactoryEntry canvas_items[] = 
{
  { "/Window", NULL, NULL, 0, "<Branch>" },
  { "/Window/Export Postscript...", NULL, export_postscript, 0, NULL },
  { "/Window/Close", "<Control>C", hide_canvas, 0, NULL },
  { "/Style", NULL, NULL, 0, "<Branch>" },
  { "/Style/Font Size 8", NULL, set_font_size, 8, "<RadioItem>" },
  { "/Style/Font Size 10", NULL, set_font_size, 10, "/Style/Font Size 8" },
  { "/Style/Font Size 12", NULL, set_font_size, 12, "/Style/Font Size 8" },
  { "/Style/Font Size 14", NULL, set_font_size, 14, "/Style/Font Size 8" },
  { "/Style/Font Size 18", NULL, set_font_size, 18, "/Style/Font Size 8" },
  { "/Style/Font Size 24", NULL, set_font_size, 24, "/Style/Font Size 8" }
};

static GtkItemFactoryEntry hanging_items[] =
{
  { "/Style/sep1", NULL, NULL, 0, "<Separator>" },
  { "/Style/Hanging", NULL, set_hanging, 0, "<ToggleItem>" }
};

/*---------------------------------------------------------------------------*/

canvas_t *
create_canvas( string_t title,
             string_t ps_file_name,
             rectangle_t *geometry,
             configure_func_t my_configure,
             expose_func_t my_expose,
             close_func_t my_close,
             mouse_func_t my_mouse_event,
             bool_t show_hanging_option,
             GtkItemFactoryEntry items[], 
             int_t item_count )
{
  canvas_t *canvas;
  GtkWidget *menu_bar, *table, *vbox;
  GtkItemFactory *factory; /* Item factory for menus. */
  GtkAccelGroup *accel; /* Accelerator group. */
  GdkColormap *colormap;

  canvas = new_mem( sizeof( canvas_t ) );
  canvas->ps_file_name = ps_file_name;
  canvas->configure = my_configure;
  canvas->expose = my_expose;
  canvas->close = my_close;
  canvas->mouse_event = my_mouse_event;
  canvas->font_size = font_size;

  if (canvas->font_size == 0) 
    canvas->font_size = 12;

  /* Create a toplevel window. */
  canvas->window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
  gtk_signal_connect( GTK_OBJECT( canvas->window ), "delete_event",
                  GTK_SIGNAL_FUNC( delete_event ), canvas );
  gtk_window_set_title( GTK_WINDOW( canvas->window ), title );
  if (geometry->x >= 0 && geometry->y >= 0)
    gtk_widget_set_uposition( canvas->window, geometry->x, geometry->y );
  if (geometry->width > 0 && geometry->height > 0)
    gtk_widget_set_usize( canvas->window, geometry->width, geometry->height );
  gtk_window_set_policy( GTK_WINDOW( canvas->window ), TRUE, TRUE, FALSE );
  gtk_signal_connect( GTK_OBJECT( canvas->window ), "key_press_event",
                  GTK_SIGNAL_FUNC( key_press_event ), canvas );

  /* Fill the toplevel window with a vbox. */
  vbox = gtk_vbox_new( FALSE, 0 );
  gtk_container_add( GTK_CONTAINER( canvas->window ), vbox );

  /* Add a menu bar to the vbox. */
  accel = gtk_accel_group_new();
  factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "<main>", accel );
  gtk_item_factory_create_items( 
    factory, sizeof( canvas_items ) / sizeof( canvas_items[0] ),
    canvas_items, canvas );
  if (show_hanging_option)
  {
    gtk_item_factory_create_items(
      factory, sizeof( hanging_items ) / sizeof( hanging_items[0] ),  
      hanging_items, canvas );
  }
  if (items != NULL)
    gtk_item_factory_create_items( factory, item_count, items, canvas );
  gtk_window_add_accel_group( GTK_WINDOW( canvas->window ), accel );
  menu_bar = gtk_item_factory_get_widget( factory, "<main>" );
  if (menu_bar == NULL) 
    complain( "Could not create menu bar." );
  gtk_box_pack_start( GTK_BOX( vbox ), menu_bar, FALSE, FALSE, 0 );

  /* Add a table with 2 rows and 2 columns to the vbox. */
  table = gtk_table_new( 2, 2, FALSE );
  gtk_box_pack_start_defaults( GTK_BOX( vbox ), table );
  
  /* Add a drawing area to the table in row 1, column 1. */
  canvas->draw_area = gtk_drawing_area_new();
  gtk_signal_connect( GTK_OBJECT( canvas->draw_area ), "expose_event",
                  GTK_SIGNAL_FUNC( expose_event ), canvas );
  gtk_signal_connect( GTK_OBJECT( canvas->draw_area ), "configure_event",
                  GTK_SIGNAL_FUNC( configure_event ), canvas );
  gtk_table_attach( GTK_TABLE( table ), canvas->draw_area, 0, 1, 0, 1,
                GTK_FILL, GTK_FILL, 0, 0 );
  
  /* Add a vscrollbar to the table in row 1, column 2. */
  canvas->vadjust = GTK_ADJUSTMENT( gtk_adjustment_new( 0, 0, 0, 10, 0, 0 ) );
  gtk_signal_connect( GTK_OBJECT( canvas->vadjust ), "value_changed",
                  GTK_SIGNAL_FUNC( adjust_value_changed ), canvas );
  canvas->vscrollbar = gtk_vscrollbar_new( canvas->vadjust );
  gtk_table_attach( GTK_TABLE( table ), canvas->vscrollbar, 1, 2, 0, 1,
                0, GTK_FILL | GTK_EXPAND, 0, 0 );

  /* Add a hscrollbar to the table in row 2, column 1. */
  canvas->hadjust = GTK_ADJUSTMENT( gtk_adjustment_new( 0, 0, 0, 10, 0, 0 ) );
  gtk_signal_connect( GTK_OBJECT( canvas->hadjust ), "value_changed",
                  GTK_SIGNAL_FUNC( adjust_value_changed ), canvas );
  canvas->hscrollbar = gtk_hscrollbar_new( canvas->hadjust );
  gtk_table_attach( GTK_TABLE( table ), canvas->hscrollbar, 0, 1, 1, 2,
                GTK_FILL | GTK_EXPAND, 0, 0, 0 );
  
  if (my_mouse_event != NULL)
  {
    gtk_signal_connect( GTK_OBJECT( canvas->draw_area ), "button_press_event",
                  GTK_SIGNAL_FUNC( mouse_event ), canvas );
    gtk_signal_connect( GTK_OBJECT( canvas->draw_area ), "motion_notify_event",
                  GTK_SIGNAL_FUNC( mouse_event ), canvas );
    gtk_signal_connect( GTK_OBJECT( canvas->draw_area ), "enter_notify_event",
                  GTK_SIGNAL_FUNC( mouse_event ), canvas );
    gtk_widget_add_events( canvas->draw_area, 
                     GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK
                     | GDK_ENTER_NOTIFY_MASK);
  }

  if (alternate_cursor == NULL) 
    alternate_cursor = gdk_cursor_new( GDK_HAND2 );

  configure_canvas( canvas );
  gtk_widget_show_all( canvas->window );

  /* Select the right font size menu item. */
  gtk_menu_item_activate( 
    GTK_MENU_ITEM( 
      gtk_item_factory_get_widget_by_action( factory, canvas->font_size ) ) );

  /* Disable the Postscript menu item if character set is not supported. */
  if (! hangul && strcmp_no_case( char_set, "iso8859-1" ) != 0)
  {
    gtk_widget_set_sensitive(
      gtk_item_factory_get_widget( factory, "/Window/Export Postscript..." ), 
       0);
  }

  if (gc == NULL)
  { 
    /* Allocate the shared graphics context and the shared colors. */
    gc = gdk_gc_new( gtk_widget_get_parent_window( canvas->draw_area ) );
    colormap = gtk_widget_get_colormap( canvas->draw_area );
    if (! gdk_color_black( colormap, &colors[ BLACK ] )) 
      complain( "Could not get black." );
    if (! gdk_color_white( colormap, &colors[ WHITE ] ))
      complain( "Could not get white." );
    colors[ RED ].red = 65535; 
    colors[ RED ].green = colors[ RED ].blue = 0;
    if (! gdk_colormap_alloc_color( colormap, &colors[ RED ], FALSE, TRUE ))
      complain( "Could not get red." );
  }
  return canvas;
}

/*---------------------------------------------------------------------------*/

pos_string_t *
new_pos_string( string_t string )
/* Create new POS_STRING_T with value STRING. */
{
  _pos_string_t *_pos_string;

  _pos_string = new_mem( sizeof( _pos_string_t ) );
  _pos_string->string = (string == NULL) ? NULL : new_string( string, NULL );
  return (pos_string_t *) _pos_string;
}

/*---------------------------------------------------------------------------*/

void 
config_pos_string( pos_string_t *pos_string, canvas_t *canvas )
/* Compute size of POS_STRING in CANVAS. */
{
  _pos_string_t *_pos_string = (_pos_string_t *) pos_string;

  _pos_string->width = (_pos_string->string == NULL ? 0 : 
                  string_width( canvas, _pos_string->string ));
}

/*---------------------------------------------------------------------------*/

void 
draw_pos_string( pos_string_t *pos_string, canvas_t *canvas )
/* Draw POS_STRING in CANVAS. */
{
  _pos_string_t *_pos_string = (_pos_string_t *) pos_string;

  if (_pos_string->string == NULL) 
    return;
  draw_string( canvas, _pos_string->string, _pos_string->x, _pos_string->y, 
             _pos_string->width );
}

/*---------------------------------------------------------------------------*/

void 
free_pos_string( pos_string_t **pos_string_p )
/* Free memory allocated by "set_pos_string". */
{
  _pos_string_t *_pos_string = (_pos_string_t *) *pos_string_p;

  if (_pos_string != NULL) 
    free_mem( &_pos_string->string );
  free_mem( pos_string_p );
}

/*---------------------------------------------------------------------------*/

pos_value_t *
parse_pos_value( void )
{
  _pos_value_t *_pos_value;

  _pos_value = new_mem( sizeof( _pos_value_t ) );
  _pos_value->value = parse_value();
  return (pos_value_t *) _pos_value;
}

/*---------------------------------------------------------------------------*/

void 
config_pos_value( pos_value_t *pos_value, canvas_t *canvas )
/* Compute size of POS_VALUE in CANVAS. */
{
  _pos_value_t *_pos_value = (_pos_value_t *) pos_value;

  config_value_size( canvas, _pos_value->value );
  _pos_value->width = _pos_value->value->width;
  _pos_value->height = _pos_value->value->height;
  _pos_value->ascent = _pos_value->value->ascent;
}

/*---------------------------------------------------------------------------*/

void 
draw_pos_value( pos_value_t *pos_value, canvas_t *canvas )
/* Draw POS_VALUE in CANVAS. */
{
  _pos_value_t *_pos_value = (_pos_value_t *) pos_value;

  draw_value( canvas, _pos_value->value, _pos_value->x, _pos_value->y );
}

/*---------------------------------------------------------------------------*/

void
free_pos_value( pos_value_t **pos_value_p )
{
  _pos_value_t *_pos_value = (_pos_value_t *) *pos_value_p;

  if (_pos_value != NULL) 
    free_value( &_pos_value->value );
  free_mem( pos_value_p );
}

/* End of file. =============================================================*/

Generated by  Doxygen 1.6.0   Back to index