/* Copyright (C) 2002 Bjoern Beutel. */ /* Description. =============================================================*/ /* Common routines for all Malaga GTK windows. */ /* Includes. ================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "basic.h" #include "scanner.h" #include "input.h" #include "files.h" #include "canvas.h" /* Constants. ===============================================================*/ enum {BRACKET_RATIO = 6}; /* Height:width ratio of angle brackets. */ enum {BUFFER_SIZE = 200}; /* Size of Postscript 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) /* Hangul code points in Unicode. */ enum {FIRST_JAMO = 0x3131, LAST_JAMO = 0x3163, JAMO_COUNT = (LAST_JAMO - FIRST_JAMO + 1), FIRST_SYLLABLE = 0xac00, LAST_SYLLABLE = 0xd7a3, SYLLABLE_COUNT = (LAST_SYLLABLE - FIRST_SYLLABLE + 1)}; /* Code points in the N3F Hangul Postscript font. */ enum {FIRST_CHOSEONG = 162, CHOSEONG_COUNT = 19, NO_CHOSEONG = 161, FIRST_JUNGSEONG = 182, JUNGSEONG_COUNT = 21, NO_JUNGSEONG = 181, FIRST_JONSEONG = 203, JONSEONG_COUNT = 28}; /*---------------------------------------------------------------------------*/ /* This table maps Unicode Hangul Jamos to the N3F encoding. It starts at * code point FIRST_JAMO. */ static char_t jamos[JAMO_COUNT] = { 162, 163, 205, 164, 207, 208, 165, 166, 167, 211, 212, 213, 214, 215, 216, 217, 168, 169, 170, 220, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202 }; /*---------------------------------------------------------------------------*/ /* Widths of Adobe Helvetica characters in Latin1 encoding. */ static int widths_latin1[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 }; /* Widths of N3F-5 chars in N3F Encoding. Table starts at code point * FIRST_N3F. */ enum {FIRST_N3F = 161, LAST_N3F = 229, N3F_COUNT = LAST_N3F - FIRST_N3F + 1}; static int widths_n3f[N3F_COUNT] = { 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 }; /*---------------------------------------------------------------------------*/ static const char n3f_font_definition[] = "%%BeginResource: font n3f-5\n" "%\n" "% Copyright 1996, 1998 Lee Yongjae \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"; /* 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. */ PangoLayout *string; /* String (for GSTRING_TYPE). */ PangoLayout *attribute; /* Attribute name (if record element). */ int_t attrib_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_i_t; typedef struct /* Implementation type of "pos_string_t". */ { int_t x, y, width, height, ascent; /* Private items follow. */ PangoLayout *layout; } pos_string_i_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; GtkItemFactory *factory; /* Item factory for the pulldown menu. */ GtkItemFactory *popup_menu_factory; GdkEventButton *event; /* Button press event currently handled. */ bool_t show_hscrollbar, show_vscrollbar; int_t vscrollbar_width, hscrollbar_height; 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; PangoFontDescription *font; int_t font_size; /* Selected font size. */ int_t font_ascent; /* Ascent of FONT. */ int_t font_height; /* Height of a line in 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 line_width; /* Width of canvas lines. */ int_t border_height; /* Height of list and record borders. */ bool_t hanging_style; /* TRUE if values are "hanging down" from baseline. */ bool_t alternate_cursor; /* TRUE if alternate cursor is displayed. */ PangoLayout *comma_layout, *space_layout; }; /* Global variables. ========================================================*/ string_t font_family; int_t font_size; bool_t hanging_style; /* Variables. ===============================================================*/ /* The following variables can be shared among all canvases since there is * always only one canvas being drawn. */ static char_t ps_string[ BUFFER_SIZE ]; /* Converted PostScript string. */ static GdkDrawable *drawable; /* Drawable we currently draw into. */ static cairo_t *cairo; /* Cairo context for DRAWABLE. */ static rectangle_t area; /* The clipping rectangle of DRAWABLE, using the * coordinates of the whole canvas. */ static double canvas_shift; /* Shift if stroke width is odd. */ static GdkRectangle clip; /* The clipping rectangle of DRAWABLE, using the * coordinates of DRAWABLE. */ static GdkGC *gc; /* Graphics context used to draw. */ static GdkColor colors[4]; /* BLACK, WHITE, RED and BLUE. */ static FILE *ps_stream; /* Stream used for PostScript output. */ static bool_t ps_mode; /* TRUE => draw into PS_STREAM; * FALSE => draw into DRAWABLE. */ static bool_t use_n3f; /* TRUE iff we'll output N3F Hangul characters to * PS_STREAM. */ /* The following variables can be shared among all canvases since they are * only set up at initialisation. */ static GdkCursor *alternate_cursor; static PangoContext *pango_context; /* 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 ); if (value->attribute != NULL) g_object_unref( value->attribute ); if (value->string != NULL) g_object_unref( 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 void value_set_string( gvalue_t *value, char_t *string ) { if (pango_context == NULL) pango_context = gdk_pango_context_get(); value->value_type = GSTRING_TYPE; value->string = pango_layout_new( pango_context ); pango_layout_set_text( value->string, string, -1 ); free( string ); } /*---------------------------------------------------------------------------*/ static void value_set_attribute( gvalue_t *value, char_t *attribute ) { if (pango_context == NULL) pango_context = gdk_pango_context_get(); value->attribute = pango_layout_new( pango_context ); pango_layout_set_text( value->attribute, attribute, -1 ); free( attribute ); } /*---------------------------------------------------------------------------*/ 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 ) ); value_set_attribute( new_value, concat_strings( attribute, ":", NULL ) ); value_set_string( new_value, new_string( "...", NULL ) ); free_mem( &attribute ); } else { attribute = parse_symbol(); parse_token( ':' ); new_value = parse_value(); value_set_attribute( new_value, 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: value_set_string( new_value, new_string( token_name, NULL ) ); read_next_token(); break; case TOK_STRING: value_set_string( new_value, new_string_readable( token_string, NULL ) ); read_next_token(); break; case TOK_NUMBER: value_set_string( new_value, double_to_string( token_number ) ); read_next_token(); break; case '-': read_next_token(); test_token( TOK_NUMBER ); value_set_string( new_value, 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_line_width( canvas_t *canvas ) /* Get line width in pixels for CANVAS. */ { return canvas->line_width; } /*---------------------------------------------------------------------------*/ 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 ] ); gdk_cairo_set_source_color( cairo, &colors[ color ] ); } } /*---------------------------------------------------------------------------*/ static bool_t clip_line( double *x1, double *y1, double *x2, double *y2, double x_min, double x_max ) /* Clip the x-coordinates of the line from (X1,Y1) to (X2,Y2) to the range * [X_MIN..X_MAX]. The start point and the end point may be swapped. */ { double t; /* Make sure that *X1 <= *X2. */ if (*x2 < *x1) { t = *x1; *x1 = *x2; *x2 = t; t = *y1; *y1 = *y2; *y2 = t; } if (*x2 < x_min || *x1 > x_max) return FALSE; if (*x1 < x_min) { /* We can be sure that *X1 != *X2, since *X1 < X_MIN, but *X2 >= X_MIN. */ *y1 += ((x_min - *x1) * (*y2 - *y1)) / (*x2 - *x1); *x1 = x_min; } if (*x2 > x_max) { /* We can be sure that *X1 != *X2, since *X2 > X_MAX, but *X1 <= X_MAX. */ *y2 += ((x_max - *x2) * (*y2 - *y1)) / (*x2 - *x1); *x2 = x_max; } return TRUE; } /*---------------------------------------------------------------------------*/ static void draw_lines_clipped( GdkPoint p[], int count ) /* Draw lines from p[0] TO p[1], p[1] TO p[2], ..., p[count-2] TO p[count-1]. * Clip coordinates that are out of the current clip range. * * This algorithm clips to the exact start and end points, but it cannot * guarantee that the line is translational invariant: If a clipped line is * drawn from (x1, y1) to (x2, y2) and another line from (x1 + dx, y1 + dy) to * (x2 + dx, y2 + dy), we cannot guarantee that, for each point (x, y) which is * part of the first line, the point (x + dx, y + dy) is part of the second * line, and vice versa. */ { int_t i; double x1, y1, x2, y2; const double left = clip.x - 10; const double right = clip.x + clip.width + 10; const double top = clip.y - 10; const double bottom = clip.y + clip.height + 10; for (i = 1; i < count; i++) { x1 = p[i-1].x; y1 = p[i-1].y; x2 = p[i].x; y2 = p[i].y; if (clip_line( &x1, &y1, &x2, &y2, left, right) && clip_line( &y1, &x1, &y2, &x2, top, bottom)) { cairo_move_to( cairo, x1 + canvas_shift, y1 + canvas_shift); cairo_line_to( cairo, x2 + canvas_shift , y2 + canvas_shift); } } } /*---------------------------------------------------------------------------*/ 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, need_clipping; 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; x += clip.x; y += clip.y; if (x < SHRT_MIN || x > SHRT_MAX || y < SHRT_MIN || y > SHRT_MAX) need_clipping = 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) { cairo_new_path( cairo ); if (need_clipping) draw_lines_clipped( p, count ); else { for (i = 0; i < count; i++) cairo_line_to(cairo, p[i].x + canvas_shift, p[i].y + canvas_shift); } cairo_stroke( cairo ); } } } /*---------------------------------------------------------------------------*/ void draw_rectangle( int_t x, int_t y, int_t width, int_t height ) /* Draw a rectangle with NW coordinate (X,Y), WIDTH pixels wide and HEIGHT * pixels high. */ { 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) { cairo_new_path( cairo ); cairo_rectangle( cairo, clip.x + x + canvas_shift, clip.y + y + canvas_shift, width, height ); cairo_fill( cairo ); } } } /*---------------------------------------------------------------------------*/ 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) { cairo_new_path( cairo ); cairo_arc( cairo, clip.x + x + canvas_shift, clip.y + y + canvas_shift, r, 0, 2 * M_PI ); cairo_close_path( cairo ); if (filled) cairo_fill( cairo ); else cairo_stroke( cairo ); } } } /*---------------------------------------------------------------------------*/ static void utf8_string_to_n3f( const char **string ) /* Convert *STRING (in UTF-8) to N3F PostScript string. * Return its length. The converted string is in PS_STRING. */ { int_t c, d, choseong, jungseong, jonseong; char *ps; ps = ps_string; while (**string != EOS && ps - ps_string < BUFFER_SIZE - 4) { c = g_utf8_get_char( *string ); if (c >= FIRST_SYLLABLE && c <= LAST_SYLLABLE) { c -= FIRST_SYLLABLE; d = c / JONSEONG_COUNT; jonseong = c % JONSEONG_COUNT; jungseong = d % JUNGSEONG_COUNT; choseong = d / JUNGSEONG_COUNT; *ps++ = choseong + FIRST_CHOSEONG; *ps++ = jungseong + FIRST_JUNGSEONG; if (jonseong > 0) *ps++ = jonseong - 1 + FIRST_JONSEONG; } else if (c >= FIRST_JAMO && c <= LAST_JAMO) { c = jamos[ c - FIRST_JAMO ]; *ps++ = (c < FIRST_JUNGSEONG ? c : NO_CHOSEONG); *ps++ = (c >= FIRST_JUNGSEONG && c < FIRST_JONSEONG ? c : NO_JUNGSEONG); if (c >= FIRST_JONSEONG) *ps++ = c; } else break; *string = g_utf8_next_char( *string ); } *ps = EOS; } /*---------------------------------------------------------------------------*/ static void utf8_string_to_latin1( const char **string ) { int_t c; char *ps; ps = ps_string; while (**string != EOS && ps - ps_string < BUFFER_SIZE - 2) { c = g_utf8_get_char( *string ); if ((c >= 32 && c <= 126) || (c >= 160 && c <= 255)) *ps++ = c; else break; *string = g_utf8_next_char( *string ); } *ps = EOS; } /*---------------------------------------------------------------------------*/ static int_t ps_string_width( const char *string ) { int_t width; const char *s, *old_pos; width = 0; while (*string != EOS) { old_pos = string; /* Convert a Latin1 segment. */ utf8_string_to_latin1( &string ); for (s = ps_string; *s != EOS; s++) width += widths_latin1[ ORD(*s) ]; /* Convert a Hangul segment. */ utf8_string_to_n3f( &string ); if (*s != EOS) use_n3f = TRUE; for (s = ps_string; *s != EOS; s++) width += widths_n3f[ ORD(*s) - FIRST_N3F ]; /* Ignore a non-convertible character. */ if (string == old_pos && string != EOS) string = g_utf8_next_char( string ); } return width; } /*---------------------------------------------------------------------------*/ static void print_ps_text( const char *string ) /* Convert STRING to Postscript format: prefix "(", ")", and "\" with "\", * insert line breaks if line gets too long. */ { int_t col; const char *s, *old_pos; while (*string != EOS) { old_pos = string; /* Print a Latin1 segment. */ utf8_string_to_latin1( &string ); if (*ps_string != EOS) { fprintf( ps_stream, "(" ); col = 1; for (s = ps_string; *s != EOS; 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" ); } /* Print a Hangul segment. */ utf8_string_to_n3f( &string ); if (*ps_string != EOS) { fprintf( ps_stream, "<" ); col = 1; for (s = ps_string; *s != EOS; 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" ); } /* Ignore a non-convertible character. */ if (string == old_pos && string != EOS) string = g_utf8_next_char( string ); } } /*---------------------------------------------------------------------------*/ static void config_layout( canvas_t *canvas, PangoLayout *layout, int_t *width, int_t *height, int_t *ascent ) { PangoLayoutIter *iter; if (ps_mode) { if (width != NULL) *width = ps_string_width( pango_layout_get_text( layout ) ); if (height != NULL) *height = canvas->font_height; if (ascent != NULL) *ascent = canvas->font_ascent; } else { pango_layout_set_font_description( layout, canvas->font ); pango_layout_get_pixel_size( layout, width, height ); if (ascent != NULL) { iter = pango_layout_get_iter( layout ); *ascent = pango_layout_iter_get_baseline( iter ) / PANGO_SCALE; pango_layout_iter_free( iter ); } } } /*---------------------------------------------------------------------------*/ static void draw_layout( canvas_t *canvas, PangoLayout *layout, int_t x, int_t y ) /* Draw LAYOUT at position (X,Y) in CANVAS. */ { int_t width, height; if (ps_mode) { fprintf( ps_stream, "%d %d M\n", x, area.height - y - canvas->font_ascent ); print_ps_text( pango_layout_get_text( layout ) ); return; } else { pango_layout_get_pixel_size( layout, &width, &height ); x -= area.x; y -= area.y; if (x + width <= 0 || x >= area.width) return; if (y + height <= 0 || y >= area.height) return; gdk_draw_layout( drawable, gc, clip.x + x, clip.y + y, layout ); } } /*---------------------------------------------------------------------------*/ static void config_value( canvas_t *canvas, gvalue_t *value ) /* Compute the height, width and ascent of VALUE in CANVAS. */ { gvalue_t *element; int_t max_attrib_width, max_width, descent; if (value->attribute != NULL) config_layout( canvas, value->attribute, &value->attrib_width, NULL, NULL); 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( canvas, element ); max_attrib_width = MAX( max_attrib_width, element->attrib_width ); max_width = MAX( max_width, element->width ); /* Update ascent and height */ if (! canvas->hanging_style) { value->ascent = (canvas->border_height + value->height + element->ascent); } value->height += element->height; } if (canvas->hanging_style && 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( 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: config_layout( canvas, value->string, &value->width, &value->height, &value->ascent ); break; } } /*---------------------------------------------------------------------------*/ static int_t max_attribute_width( gvalue_t *value ) /* Compute maximum attribute width of record VALUE. */ { gvalue_t *element; int_t max_width; max_width = 0; for (element = value->first; element != NULL; element = element->next) max_width = MAX( max_width, element->attrib_width ); return max_width; } /*---------------------------------------------------------------------------*/ 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_layout( canvas, element->attribute, x + canvas->border_width, y + (element->height - canvas->font_height) / 2 ); /* 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_layout( canvas, canvas->comma_layout, x, y - canvas->font_ascent ); x += (canvas->comma_width + canvas->space_width); } } break; case GSTRING_TYPE: draw_layout( canvas, value->string, x, y ); break; } } /*---------------------------------------------------------------------------*/ static gboolean expose_event( GtkWidget *widget, GdkEventExpose *event, canvas_t *canvas ) { GdkColormap *colormap; 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 ); colors[ BLACK ].red = colors[ BLACK ].green = colors[ BLACK ].blue = 0; if (! gdk_colormap_alloc_color( colormap, &colors[ BLACK ], FALSE, TRUE )) complain( "Could not get black." ); colors[ WHITE ].red = colors[ WHITE ].green = colors[ WHITE ].blue = 65535; if (! gdk_colormap_alloc_color( colormap, &colors[ WHITE ], FALSE, TRUE )) 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." ); colors[ BLUE ].blue = 65535; colors[ BLUE ].red = colors[ BLUE ].green = 0; if (! gdk_colormap_alloc_color( colormap, &colors[ BLUE ], FALSE, TRUE )) complain( "Could not get blue." ); } area.x = canvas->x + event->area.x; area.y = canvas->y + event->area.y; clip.x = event->area.x; clip.y = event->area.y; clip.width = area.width = event->area.width; clip.height = area.height = event->area.height; drawable = canvas->draw_area->window; cairo = gdk_cairo_create(drawable); cairo_set_line_width( cairo, canvas->line_width ); cairo_set_line_cap( cairo, CAIRO_LINE_CAP_SQUARE ); cairo_set_line_join( cairo, CAIRO_LINE_JOIN_BEVEL ); canvas->expose( canvas, &area ); cairo_destroy(cairo); cairo = NULL; return TRUE; } /*---------------------------------------------------------------------------*/ static void configure_draw_area( canvas_t *canvas ) { if (pango_context == NULL) pango_context = gdk_pango_context_get(); if (canvas->comma_layout == NULL) { canvas->comma_layout = pango_layout_new( pango_context ); pango_layout_set_text( canvas->comma_layout, ",", -1 ); } if (canvas->space_layout == NULL) { canvas->space_layout = pango_layout_new( pango_context ); pango_layout_set_text( canvas->space_layout, " ", -1 ); } 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 = 1150; canvas->line_width = 25; } else { config_layout( canvas, canvas->space_layout, &canvas->space_width, &canvas->font_height, &canvas->font_ascent ); config_layout( canvas, canvas->comma_layout, &canvas->comma_width, NULL, NULL ); canvas->line_width = (canvas->font_height + 8) / 12; canvas->border_width = 6 * canvas->line_width; canvas->border_height = 3 * canvas->line_width; canvas_shift = ((canvas->line_width & 1) != 0) ? 0.5 : 0.0; } 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 ( use_n3f ) fprintf( ps_stream, "%%%%DocumentSuppliedResources: font n3f-5\n" ); fprintf( ps_stream, "%%%%DocumentNeededResources: font Helvetica\n" "%%%%EndComments\n" ); if ( use_n3f ) fprintf( ps_stream, "%s", n3f_font_definition ); fprintf( ps_stream, "save 11 dict begin\n" "/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" "/Helvetica-ISOLatin1\n" "/Helvetica findfont\n" "dup length dict copy\n" "dup /FID undef\n" "dup /Encoding ISOLatin1Encoding put\n" "definefont pop\n" "/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" ); if (use_n3f) fprintf( ps_stream, "/H { /n3f-5 1000 selectfont show } bind def\n" ); fprintf( ps_stream, "/L /lineto load def\n" "/M /moveto load def\n" "/R /rlineto load def\n" "/S /stroke load def\n" "/T { /Helvetica-ISOLatin1 1000 selectfont show } bind def\n" ); fprintf( ps_stream, "%d setlinewidth\n", canvas->line_width ); 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 = (string_t) gtk_file_selection_get_filename( GTK_FILE_SELECTION( canvas->file_selection ) ); TRY { ps_stream = open_stream( file_name, "w" ); ps_mode = TRUE; use_n3f = FALSE; 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", GTK_SIGNAL_FUNC( save_postscript ), canvas ); gtk_signal_connect_object( GTK_OBJECT( GTK_FILE_SELECTION( canvas->file_selection )->cancel_button ), "clicked", GTK_SIGNAL_FUNC( gtk_widget_hide ), GTK_OBJECT( canvas->file_selection ) ); gtk_signal_connect( GTK_OBJECT( canvas->file_selection ), "delete_event", GTK_SIGNAL_FUNC( 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; gtk_adjustment_changed( canvas->hadjust ); gtk_adjustment_changed( canvas->vadjust ); /* 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 ); } gtk_widget_queue_draw( canvas->draw_area ); } /*---------------------------------------------------------------------------*/ 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 ); 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_style( canvas_t *canvas, guint action, GtkWidget *item ) { canvas->hanging_style = GTK_CHECK_MENU_ITEM( item )->active; configure_canvas( canvas ); } /*---------------------------------------------------------------------------*/ static void configure_scrollbars( canvas_t *canvas ) { int_t width, height; bool_t show_hscrollbar, show_vscrollbar; width = canvas->area_width; if (canvas->show_vscrollbar) width += canvas->vscrollbar_width; show_hscrollbar = (canvas->width > width); height = canvas->area_height; if (canvas->show_hscrollbar) height += canvas->hscrollbar_height; show_vscrollbar = (canvas->height > height); if (show_vscrollbar && canvas->width + canvas->vscrollbar_width > width) show_hscrollbar = TRUE; if (show_hscrollbar && canvas->height + canvas->hscrollbar_height > height) show_vscrollbar = TRUE; if (show_hscrollbar != canvas->show_hscrollbar) { if (show_hscrollbar) gtk_widget_show( canvas->hscrollbar ); else gtk_widget_hide( canvas->hscrollbar ); canvas->show_hscrollbar = show_hscrollbar; } if (show_vscrollbar != canvas->show_vscrollbar) { if (show_vscrollbar) gtk_widget_show( canvas->vscrollbar ); else gtk_widget_hide( canvas->vscrollbar ); canvas->show_vscrollbar = show_vscrollbar; } } /*---------------------------------------------------------------------------*/ 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; configure_scrollbars( canvas ); 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; if (canvas->font != NULL) pango_font_description_free( canvas->font ); font_descriptor = new_text(); print_text( font_descriptor, "%s %d", font_family, canvas->font_size ); canvas->font = pango_font_description_from_string( font_descriptor->buffer ); free_text (&font_descriptor ); configure_draw_area( canvas ); configure_scrollbars( canvas ); adjust_canvas( canvas ); } /*---------------------------------------------------------------------------*/ 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 || canvas->draw_area->window == NULL) { return; } if (alternate_cursor == NULL) alternate_cursor = gdk_cursor_new( GDK_HAND2 ); gdk_window_set_cursor( canvas->draw_area->window, alternate ? alternate_cursor : NULL ); canvas->alternate_cursor = alternate; } /*---------------------------------------------------------------------------*/ void set_popup_menu( canvas_t *canvas, GtkItemFactoryEntry items[], int_t item_count ) /* Set a pop-up menu for CANVAS, consisting of ITEM_COUNT ITEMS. */ { canvas->popup_menu_factory = gtk_item_factory_new( GTK_TYPE_MENU, "
", NULL ); gtk_item_factory_create_items( canvas->popup_menu_factory, item_count, items, canvas ); } /*---------------------------------------------------------------------------*/ void popup_menu( canvas_t *canvas ) /* Pop up the pop-up menu for CANVAS as a reaction of a button press event that * is currently handled. */ { if (canvas->event != NULL) { gtk_item_factory_popup( canvas->popup_menu_factory, canvas->event->x_root, canvas->event->y_root, canvas->event->button, canvas->event->time ); } } /*---------------------------------------------------------------------------*/ static gboolean scroll_event( GtkWidget *widget, GdkEventScroll *event, canvas_t *canvas ) { switch (event->direction) { case GDK_SCROLL_UP: canvas->y -= canvas->area_height / 9; adjust_canvas( canvas ); return TRUE; case GDK_SCROLL_DOWN: canvas->y += canvas->area_height / 9; adjust_canvas( canvas ); return TRUE; default: return FALSE; } } /*---------------------------------------------------------------------------*/ static gboolean button_press_event( GtkWidget *widget, GdkEventButton *event, canvas_t *canvas ) { canvas->event = event; return canvas->mouse_event( canvas, ((int_t) event->x) + canvas->x, ((int_t) event->y) + canvas->y, event->button ); canvas->event = NULL; } /*---------------------------------------------------------------------------*/ static gboolean motion_notify_event( GtkWidget *widget, GdkEventMotion *event, canvas_t *canvas ) { return canvas->mouse_event( canvas, ((int_t) event->x) + canvas->x, ((int_t) event->y) + canvas->y, 0 ); } /*---------------------------------------------------------------------------*/ static gboolean enter_notify_event( GtkWidget *widget, GdkEventCrossing *event, canvas_t *canvas ) { return canvas->mouse_event( canvas, ((int_t) event->x) + canvas->x, ((int_t) event->y) + canvas->y, 0 ); } /*---------------------------------------------------------------------------*/ /* General menu items. */ static GtkItemFactoryEntry canvas_items[] = { { "/Window", NULL, NULL, 0, "" }, { "/Window/Export Postscript...", NULL, export_postscript, 0, NULL }, { "/Window/Close", "C", hide_canvas, 0, NULL }, { "/Style", NULL, NULL, 0, "" }, { "/Style/Font Size 8", NULL, set_font_size, 8, "" }, { "/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_style_items[] = { { "/Style/sep1", NULL, NULL, 0, "" }, { "/Style/Hanging", NULL, set_hanging_style, 0, "" } }; /*---------------------------------------------------------------------------*/ 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, *vbox, *table; GtkAccelGroup *accel; /* Accelerator group. */ GtkRequisition requisition; 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 = 10; /* 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_window_move( GTK_WINDOW( canvas->window ), geometry->x, geometry->y ); if (geometry->width > 0 && geometry->height > 0) { gtk_window_set_default_size( GTK_WINDOW( 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(); canvas->factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "
", accel ); gtk_item_factory_create_items( canvas->factory, ARRAY_LENGTH( canvas_items ), canvas_items, canvas ); if (show_hanging_option) { gtk_item_factory_create_items( canvas->factory, ARRAY_LENGTH( hanging_style_items ), hanging_style_items, canvas ); } if (items != NULL) gtk_item_factory_create_items( canvas->factory, item_count, items, canvas ); gtk_window_add_accel_group( GTK_WINDOW( canvas->window ), accel ); menu_bar = gtk_item_factory_get_widget( canvas->factory, "
" ); 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_signal_connect( GTK_OBJECT( canvas->draw_area ), "scroll_event", GTK_SIGNAL_FUNC( scroll_event ), canvas ); gtk_widget_add_events( canvas->draw_area, GDK_SCROLL_MASK ); if (my_mouse_event != NULL) { gtk_signal_connect( GTK_OBJECT( canvas->draw_area ), "button_press_event", GTK_SIGNAL_FUNC( button_press_event ), canvas ); gtk_signal_connect( GTK_OBJECT( canvas->draw_area ), "motion_notify_event", GTK_SIGNAL_FUNC( motion_notify_event ), canvas ); gtk_signal_connect( GTK_OBJECT( canvas->draw_area ), "enter_notify_event", GTK_SIGNAL_FUNC( enter_notify_event ), canvas ); gtk_widget_add_events( canvas->draw_area, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK ); } gtk_table_attach( GTK_TABLE( table ), canvas->draw_area, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 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_widget_size_request( canvas->vscrollbar, &requisition ); canvas->vscrollbar_width = requisition.width; gtk_table_attach( GTK_TABLE( table ), canvas->vscrollbar, 1, 2, 0, 1, 0, GTK_FILL, 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_widget_size_request( canvas->hscrollbar, &requisition ); canvas->hscrollbar_height = requisition.height; gtk_table_attach( GTK_TABLE( table ), canvas->hscrollbar, 0, 1, 1, 2, GTK_FILL, 0, 0, 0 ); /* Select some menu items. */ gtk_menu_item_activate( GTK_MENU_ITEM( gtk_item_factory_get_item_by_action( canvas->factory, canvas->font_size ) ) ); canvas->hanging_style = hanging_style; if (show_hanging_option && canvas->hanging_style) { canvas->hanging_style = TRUE; gtk_menu_item_activate( GTK_MENU_ITEM( gtk_item_factory_get_item( canvas->factory, "/Style/Hanging" ) ) ); } configure_canvas( canvas ); gtk_widget_show_all( canvas->window ); canvas->show_hscrollbar = canvas->show_vscrollbar = TRUE; return canvas; } /*---------------------------------------------------------------------------*/ void activate_menu_item( canvas_t *canvas, string_t path ) /* Activate the menu item PATH in CANVAS to VALUE. */ { gtk_menu_item_activate( GTK_MENU_ITEM( gtk_item_factory_get_item( canvas->factory, path ) ) ); } /*---------------------------------------------------------------------------*/ pos_string_t * new_pos_string( string_t string ) /* Create new POS_STRING_T with value STRING. */ { pos_string_i_t *pos_string_i; if (pango_context == NULL) pango_context = gdk_pango_context_get(); pos_string_i = new_mem( sizeof( pos_string_i_t ) ); if (string != NULL) { pos_string_i->layout = pango_layout_new( pango_context ); pango_layout_set_text( pos_string_i->layout, string, -1 ); } return (pos_string_t *) pos_string_i; } /*---------------------------------------------------------------------------*/ void config_pos_string( pos_string_t *pos_string, canvas_t *canvas ) /* Compute size of POS_STRING in CANVAS. */ { pos_string_i_t *pos_string_i = (pos_string_i_t *) pos_string; if (pos_string_i != NULL && pos_string_i->layout != NULL) { config_layout( canvas, pos_string_i->layout, &pos_string_i->width, &pos_string_i->height, &pos_string_i->ascent ); } } /*---------------------------------------------------------------------------*/ void draw_pos_string( pos_string_t *pos_string, canvas_t *canvas ) /* Draw POS_STRING in CANVAS. */ { pos_string_i_t *pos_string_i = (pos_string_i_t *) pos_string; if (pos_string_i != NULL && pos_string_i->layout != NULL) { draw_layout( canvas, pos_string_i->layout, pos_string_i->x, pos_string_i->y ); } } /*---------------------------------------------------------------------------*/ void free_pos_string( pos_string_t **pos_string_p ) /* Free memory allocated by "set_pos_string". */ { pos_string_i_t *pos_string_i = (pos_string_i_t *) *pos_string_p; if (pos_string_i != NULL && pos_string_i->layout != NULL) g_object_unref( pos_string_i->layout ); free_mem( pos_string_p ); } /*---------------------------------------------------------------------------*/ pos_value_t * parse_pos_value( void ) { pos_value_i_t *pos_value_i; pos_value_i = new_mem( sizeof( pos_value_i_t ) ); pos_value_i->value = parse_value(); return (pos_value_t *) pos_value_i; } /*---------------------------------------------------------------------------*/ void config_pos_value( pos_value_t *pos_value, canvas_t *canvas ) /* Compute size of POS_VALUE in CANVAS. */ { pos_value_i_t *pos_value_i = (pos_value_i_t *) pos_value; config_value( canvas, pos_value_i->value ); pos_value_i->width = pos_value_i->value->width; pos_value_i->height = pos_value_i->value->height; pos_value_i->ascent = pos_value_i->value->ascent; } /*---------------------------------------------------------------------------*/ void draw_pos_value( pos_value_t *pos_value, canvas_t *canvas ) /* Draw POS_VALUE in CANVAS. */ { pos_value_i_t *pos_value_i = (pos_value_i_t *) pos_value; draw_value( canvas, pos_value_i->value, pos_value_i->x, pos_value_i->y ); } /*---------------------------------------------------------------------------*/ void free_pos_value( pos_value_t **pos_value_p ) { pos_value_i_t *pos_value_i = (pos_value_i_t *) *pos_value_p; if (pos_value_i != NULL) { free_value( &pos_value_i->value ); free_mem( pos_value_p ); } } /* End of file. =============================================================*/