/* Copyright (C) 2002 Bjoern Beutel. */
/* Description. =============================================================*/
/* Common routines for all Malaga GTK windows. */
/* Includes. ================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <setjmp.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <errno.h>
#include <limits.h>
#include <pango/pango.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <cairo.h>
#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 <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";
/* 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, "<main>", 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, "<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_style_items[] =
{
{ "/Style/sep1", NULL, NULL, 0, "<Separator>" },
{ "/Style/Hanging", NULL, set_hanging_style, 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, *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, "<main>", 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, "<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_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. =============================================================*/