module ShowTimeProfile; import StdEnv; import Help; ApplicationName :== "ShowTimeProfile"; HelpFileName :== ApplicationName +++ "Help"; PCorMac pc mac :== mac; :: Profile = { module_name::String, function_name::String, n_strict_calls::Int, n_lazy_calls::Int, n_curried_calls::Int, n_allocated_words::Int, time::Real }; :: FormattedProfile = { f_module_name::String, f_function_name::String, f_n_strict_calls::Int, f_n_lazy_calls::Int, f_n_curried_calls::Int, f_n_allocated_bytes::Int, f_alloc_percentage::Real, f_time::Real, f_time_percentage::Real }; format_string_r length string # string_size=size string; | string_size >= length = string; = (createArray (length-string_size) ' ')+++string; format_real n_spaces n d m r | r<0.0 = format_negative_real (if (n_spaces<1) 0 (dec n_spaces)) n d m (~r); # s=toString (toInt (m*r)); l=size s; | l<=d = createArray n_spaces ' ' +++ createArray n '0' +++"."+++createArray (d-l) '0'+++s; | l<=n+d = createArray n_spaces ' ' +++ createArray (n+d-l) '0' +++insert_dot_in_string s l d; | l<=n_spaces+n+d = createArray (n_spaces+n+d-l) ' '+++ insert_dot_in_string s l d; = insert_dot_in_string s l d; format_negative_real n_spaces n d m r # s=toString (toInt (m*r)); l=size s; | l<=d = createArray n_spaces ' ' +++"-"+++ createArray n '0' +++"."+++ createArray (d-l) '0' +++s; | l<=n+d = createArray n_spaces ' ' +++"-"+++ createArray (n+d-l) '0' +++insert_dot_in_string s l d; | l<=n_spaces+n+d = createArray (n_spaces+n+d-l) ' ' +++ "-"+++insert_dot_in_string s l d; = "-"+++insert_dot_in_string s l d; insert_dot_in_string s l d = s % (0,l-1-d) +++"."+++ s % (l-d,l-1); format_profile total_strict_calls total_lazy_calls total_curried_calls total_allocation total_time profile_list = ([format_profile p \\ p<-profile_list], { f_module_name = "All Modules", f_function_name="Total", f_n_strict_calls=total_strict_calls, f_n_lazy_calls=total_lazy_calls, f_n_curried_calls=total_curried_calls, f_n_allocated_bytes=PCorMac total_allocation (total_allocation<<2), f_alloc_percentage=100.0, f_time=total_time, f_time_percentage=100.0 }); { format_profile {module_name,function_name,n_strict_calls,n_lazy_calls,n_curried_calls,n_allocated_words,time} = { f_module_name=module_name, f_function_name=function_name, f_time=time, f_time_percentage=(time*100.0)/total_time, f_n_allocated_bytes=PCorMac n_allocated_words (n_allocated_words<<2), f_alloc_percentage=(toReal (n_allocated_words)*100.0)/toReal total_allocation, f_n_strict_calls=n_strict_calls, f_n_lazy_calls=n_lazy_calls, f_n_curried_calls=n_curried_calls }; } sum_time_and_allocation l = foldl add_time_and_allocation (0,0,0,0,0.0) l; { add_time_and_allocation (s,l,c,a,t) {function_name,n_strict_calls,n_lazy_calls,n_curried_calls,n_allocated_words,time} | n_allocated_words>=0 = (s+n_strict_calls,l+n_lazy_calls,c+n_curried_calls,a+n_allocated_words,t+time) = (s+n_strict_calls,l+n_lazy_calls,c+n_curried_calls,a,t+time); } totals_per_module [] = []; totals_per_module [f=:{module_name}:l] # (functions,l) = split_at_next_module l; with { split_at_next_module [] = ([],[]); split_at_next_module l=:[f=:{module_name=m}:t] | m==module_name # (functions,l) = split_at_next_module t; = ([f:functions],l); = ([],l); } # functions = [f:functions]; # (total_strict_calls,total_lazy_calls,total_curried_calls,total_allocation,total_time) = sum_time_and_allocation functions; # new_module = {module_name=module_name, function_name="Module "+++module_name, n_strict_calls=total_strict_calls, n_lazy_calls=total_lazy_calls, n_curried_calls=total_curried_calls, n_allocated_words=total_allocation, time=total_time }; = [new_module:totals_per_module l]; read_profile file # (processor,processor_clock,bus_clock,file) = read_processor_information file; // # (_,clock_speed,overhead) = clock_speed_and_profile_overhead; # clock_speed=abort "read_profile"; # overhead=abort "read_profile"; = read_function_profiles (PCorMac (compute_time_x86 (clock_speed*1.0E6) overhead) (compute_time processor processor_clock bus_clock)) file; read_processor_information file # (ok,processor,file)=freadi file; | not ok = error file; # (ok,processor_clock,file)=freadi file; | not ok = error file; # (ok,bus_clock,file)=freadi file; | not ok = error file; # (ok,c,file) = freadc file; | not ok || c<>'\n' = error file; = (processor,processor_clock,bus_clock,file); {}{ error file = (0,1,1,file); } TwoPower32Real:==4294967296.0; PowerPC601GestaltNumber:==257; PowerPC750GestaltNumber:==264; PowerPC7400GestaltNumber:==268; PowerPC603604ProfileOverhead:==10.0; PowerPC750ProfileOverhead:==7.0; compute_time processor processor_clock bus_clock | processor==PowerPC601GestaltNumber = \ (time_hi,time_lo,n_profiler_calls) -> toReal time_hi + (toReal time_lo / 1E+9) - (toReal n_profiler_calls*16.0/toReal processor_clock); | processor>=PowerPC750GestaltNumber = \ (time_hi,time_lo,n_profiler_calls) -> ((toReal time_hi*TwoPower32Real + (if (time_lo>=0) (toReal time_lo) (TwoPower32Real+toReal time_lo)))*4.0)/toReal bus_clock - (toReal n_profiler_calls*PowerPC750ProfileOverhead/toReal processor_clock); = \ (time_hi,time_lo,n_profiler_calls) -> ((toReal time_hi*TwoPower32Real + (if (time_lo>=0) (toReal time_lo) (TwoPower32Real+toReal time_lo)))*4.0)/toReal bus_clock - (toReal n_profiler_calls*PowerPC603604ProfileOverhead/toReal processor_clock); compute_time_x86 processor_clock profile_overhead = \ (time_hi,time_lo,n_profiler_calls) -> (toReal time_hi*TwoPower32Real + (if (time_lo>=0) (toReal time_lo) (TwoPower32Real+toReal time_lo)))/toReal processor_clock - (toReal n_profiler_calls*profile_overhead/toReal processor_clock); read_function_profiles compute_time_function file # (ok,function_profile,file) = read_function_profile file; | not ok = ([],file); # (profile,file) = read_function_profiles compute_time_function file; = ([function_profile : profile],file); {}{ read_function_profile file # (ok,module_name,file) = read_function_name file; | not ok = error file; # (ok,function_name,file) = read_function_name file; | not ok = error file; # (ok,n_strict_calls,file)=freadi file; | not ok = error file; # (ok,n_lazy_calls,file)=freadi file; | not ok = error file; # (ok,n_curried_calls,file)=freadi file; | not ok = error file; # (ok,n_profiler_calls,file)=freadi file; | not ok = error file; # (ok,n_allocated_words,file)=freadi file; | not ok = error file; # (ok,time_hi,file)=freadi file; | not ok = error file; # (ok,time_lo,file)=freadi file; | not ok = error file; # (ok,c,file) = freadc file; | not ok || c<>'\n' = error file; # time = compute_time_function (time_hi,time_lo,n_profiler_calls); = (True,{ module_name=module_name, function_name=function_name,n_strict_calls=n_strict_calls,n_lazy_calls=n_lazy_calls, n_curried_calls=n_curried_calls,n_allocated_words=n_allocated_words,time=time},file); {}{ error file = (False,abort "error in read_function_profile",file); } read_function_name file # (ok,c,file) = freadc file; | not ok || c==' ' || c=='\n' = (False,"",file); # (_,s,file) = read_function_name file = (True,toString c+++s,file); } ge_profile_time {f_time=time1}{f_time=time2} = time1>=time2; import deltaEventIO,deltaPicture, deltaIOState; from deltaWindow import DrawInActiveWindowFrame,ChangePictureDomain,DrawInWindow; from deltaSystem import MaxFixedWindowSize; from deltaFileSelect import SelectInputFile; from deltaWindow import OpenWindows,CloseWindows; from deltaMenu import EnableMenus,DisableMenus,EnableMenuItems,DisableMenuItems; (<::) infix; (<::) f t:== f a b; { (a,b)=t }; (AP3) infix; (AP3) f t:== f a b c; { (a,b,c)=t }; (>:) infixl; (>:) f g:== g f; draw_string_at position s picture :== picture >: MovePenTo position >: DrawString s; monaco9_font # (ok,font)=SelectFont (PCorMac "Arial" "Monaco") [] 9; | ok = font; geneva6_font # (ok,font)=SelectFont (PCorMac "Arial" "Geneva") [] (PCorMac 7 6); = font; geneva8_font # (ok,font)=SelectFont (PCorMac "Arial" "Geneva") [] (PCorMac 9 8); = font; Pos0:==4; Pos1:==300; Pos2:==440; Pos3:==540; Pos4:==600; Pos5:==680; Pos6:==740; Pos7:==800; Pos8:==860; WindowWidth:==940; PPrinterPos0:==2; PPrinterPos1:==30*5; PPrinterPos2:==44*5; PPrinterPos3:==54*5; PPrinterPos4:==60*5; PPrinterPos5:==68*5; PPrinterPos6:==74*5; PPrinterPos7:==80*5; PPrinterPos8:==86*5; PPrinterWindowWidth:==94*5; LPrinterPos0:==3; LPrinterPos1:==30*8; LPrinterPos2:==44*8; LPrinterPos3:==54*8; LPrinterPos4:==60*8; LPrinterPos5:==68*8; LPrinterPos6:==74*8; LPrinterPos7:==80*8; LPrinterPos8:==86*8; LPrinterWindowWidth:==94*8; draw_table :: [FormattedProfile] [((a,.Int),(b,.Int))] {#Int} .Int .Int Font *Picture -> *Picture; draw_table profile area column_positions window_width char_width window_font p = p >: SetFont window_font >: draw_table_header (2+ascent) (2+line_height) window_width >: draw_profile_lines profile (4+ascent+line_height) line_height area; { line_height=ascent+descent+1; (ascent,descent,_,_)=FontMetrics window_font; draw_profile_lines function_profiles y line_height area picture = draw_profile_lines function_profiles y picture; { in_area y [((x1,y1),(x2,y2)):areas] = (y >= y1-line_height && y <= y2+line_height) || in_area y areas; in_area y [] = False; draw_profile_lines [] y picture = picture; draw_profile_lines [{f_module_name,f_function_name,f_time,f_time_percentage,f_n_allocated_bytes,f_alloc_percentage,f_n_strict_calls,f_n_lazy_calls,f_n_curried_calls}:function_profiles] y picture | in_area y area # picture=picture >: draw_string_at (column_positions.[0],y) (if (size f_function_name<=50) f_function_name (f_function_name%(0,47)+++"..")) >: draw_string_at (column_positions.[1],y) (if (size f_module_name<=30) f_module_name (f_module_name%(0,27)+++"..")) >: draw_string_at_left (column_positions.[2],y) (format_real 6 1 6 1000000.0 f_time) >: draw_string_at_left (column_positions.[3],y) (format_real 2 2 3 1000.0 f_time_percentage) >: draw_string_at_left (column_positions.[4],y) (format_string_r 12 (toString f_n_allocated_bytes)) >: draw_string_at_left (column_positions.[5],y) (format_real 2 2 3 1000.0 (f_alloc_percentage)) >: draw_string_at_left (column_positions.[6],y) (format_string_r 10 (toString f_n_strict_calls)) >: draw_string_at_left (column_positions.[7],y) (format_string_r 10 (toString f_n_lazy_calls)) >: draw_string_at_left (column_positions.[8],y) (format_string_r 10 (toString f_n_curried_calls)) = draw_profile_lines function_profiles (y+line_height) picture; = draw_profile_lines function_profiles (y+line_height) picture; } draw_table_header y line_y window_width picture = picture >: draw_string_at (column_positions.[0],y) "Function" >: draw_string_at (column_positions.[1],y) "Module" >: draw_string_at_left (column_positions.[2],y) " Time(s)" >: draw_string_at_left (column_positions.[3],y) " Time(%)" >: draw_string_at_left (column_positions.[4],y) (format_string_r 13 "Alloc(bytes)") >: draw_string_at_left (column_positions.[5],y) " Alloc(%)" >: draw_string_at_left (column_positions.[6],y) (format_string_r 13 "Strict(n)") >: draw_string_at_left (column_positions.[7],y) (format_string_r 13 "Lazy(n)") >: draw_string_at_left (column_positions.[8],y) (format_string_r 13 "Curried(n)") >: DrawLine ((0,line_y),(window_width,line_y)); draw_string_at_left (x,y) s picture # (string_width_in_pixels,picture) = PictureStringWidth s picture; # position=(x+(size s*char_width-string_width_in_pixels),y); = picture >: MovePenTo position >: DrawString s; } :: MPrintSetup = PrintNotSetup | MPrintSetup PrintSetup; :: *MState = PState ProfileInfo (UpdateFunction MState) MPrintSetup; :: ProfileInfo = ProfileInfo Bool [FormattedProfile] [FormattedProfile] FormattedProfile | NoProfileInfo; from deltaDialog import OpenNotice,Notice,NoticeButton; /* error_notice strings s io # (_,s,io) = OpenNotice (Notice strings (NoticeButton 0 "OK") []) s io; = (s,QuitIO io); error_notice_and_quit strings world # (_,world) = StartIO [TimerSystem []] 0 [error_notice strings] world; = world; define_fltused :: !Bool -> Bool; define_fltused n = code { .export _fltused :_fltused pop_b 0 } measure_clock_speed_and_profile_overhead :: (!Int,!Real,!Real); measure_clock_speed_and_profile_overhead = code { ccall measure_clock_speed_and_profile_overhead ":IRR" } clock_speed_and_profile_overhead | define_fltused True =: measure_clock_speed_and_profile_overhead; */ Start world /* # (r,_,_) = clock_speed_and_profile_overhead; | r==1 = error_notice_and_quit ["Profiling does not work on this computer,", "because the processor does not have a time stamp counter" ] world; | r==2 = error_notice_and_quit ["Profiling does not work on this computer,", "because the windows API function QueryPerformanceFrequency failed" ] world; */ # (aboutdialog,world) = accFiles (MakeAboutDialog ApplicationName HelpFileName show_help) world # (_,world) = let { state = PState NoProfileInfo update_function PrintNotSetup; update_function=window_update_function monaco9_font; io_system = [DialogSystem [aboutdialog], MenuSystem [file_menu,sort_menu,show_menu], AppleEventSystem { openHandler = open_file_function, quitHandler= \s io -> (s,QuitIO io), scriptHandler = \_ s io -> (s, io), clipboardChangedHandler = \s io -> (s, io)} ]; file_menu = PullDownMenu 1 "File" Able [ MenuItem 10 "Open..." (Key 'O') Able file_open_function, MenuItem 11 "Close" (Key 'W') Unable file_close_function, MenuItem 12 "Print" (Key 'P') Unable printTable: PCorMac file_menu_items_rest [MenuItem 13 "Page Setup.." NoKey Able page_setup:file_menu_items_rest]]; file_menu_items_rest =[ MenuItem 14 "Help" NoKey Able show_help, MenuItem 15 "Quit" (Key 'Q') Able (\s io -> (s,QuitIO io)) ]; sort_menu = PullDownMenu 2 "Sort" Unable [ MenuItem 20 "Sort by Function" (Key 'F') Able sort_by_function_name, MenuItem 21 "Sort by Module" (Key 'M') Able sort_by_module_name, MenuItem 22 "Sort by Time" (Key 'T') Able sort_by_time_function, MenuItem 23 "Sort by Allocation" (Key 'A') Able sort_by_allocation_function, MenuItem 24 "Sort by Strict calls" (Key 'S') Able sort_by_strict_function, MenuItem 25 "Sort by Lazy calls" (Key 'L') Able sort_by_lazy_function, MenuItem 26 "Sort by Curried calls" (Key 'C') Able sort_by_curried_function ]; show_menu = PullDownMenu 3 "Show" Unable [ MenuItem 30 "Show Functions" NoKey Able show_functions, MenuItem 31 "Show Modules" NoKey Able show_modules ]; window_update_function window_font area (PState profile=:(ProfileInfo show_functions formatted_profile other_profile total_profile) update_function print_setup) = (PState profile update_function print_setup,[draw_table (formatted_profile++[total_profile]) area column_positions WindowWidth 6 window_font]); column_positions={Pos0,Pos1,Pos2,Pos3,Pos4,Pos5,Pos6,Pos7,Pos8}; } in StartIO io_system state [] world; = world; file_open_function pstate io # (file_selected,file_name,(PState profile update_function print_setup),io) = SelectInputFile pstate io; with { select_input_file files # (file_selected,file_name,files,io)=SelectInputFile files io; = ((file_selected,file_name,io),files); } | file_selected = open_file_function file_name (PState profile update_function print_setup) io; = (PState profile update_function print_setup,io); file_close_function (PState profile_info update_function print_setup) io # io=io >: DisableMenus [2,3] >: DisableMenuItems [11,12] >: CloseWindows [0] >: EnableMenuItems [10]; = (PState NoProfileInfo update_function print_setup,io); open_profile file_name files # (open_ok,input_file,files) = fopen file_name FReadText files; | not open_ok = ((False,[]),files); # (profile,input_file)=read_profile input_file; (close_ok,files)=fclose input_file files; = ((True,profile),files); window_height_function ascent descent formatted_profile = 6+(ascent+descent+1)*(2+length formatted_profile); open_file_function file_name (PState NoProfileInfo update_function print_setup) io # ((open_ok,profile),io) = accFiles (open_profile file_name) io; | not open_ok = (PState NoProfileInfo update_function print_setup,io); # (total_strict_calls,total_lazy_calls,total_curried_calls,total_allocation,total_time) = sum_time_and_allocation profile; module_profile=totals_per_module (sortBy (\ p1 p2 -> p1.module_name<=p2.module_name) profile); (formatted_module_profile,_)=format_profile total_strict_calls total_lazy_calls total_curried_calls total_allocation total_time module_profile; formatted_module_profile=sortBy ge_profile_time formatted_module_profile; (formatted_profile,total_profile)=format_profile total_strict_calls total_lazy_calls total_curried_calls total_allocation total_time profile; formatted_profile=sortBy ge_profile_time formatted_profile; (screen_size_x,screen_size_y)=MaxFixedWindowSize; profile_window = let { window_height=window_height_function ascent descent formatted_profile; (ascent,descent,_,_)=FontMetrics monaco9_font; window_mouse_function ((x,y),ButtonDown,_) s io | y>=0 && y<3+ascent+descent | x>1,10) file_name (ScrollBar (Thumb 0) (Scroll 4)) (ScrollBar (Thumb 0) (Scroll 4)) ((0,0),(WindowWidth,window_height)) (100,10) (WindowWidth,if (window_height<=screen_size_y-40) window_height (screen_size_y-40)) update_function [Mouse Able window_mouse_function,GoAway file_close_function]; # io=io >: DisableMenuItems [10] >: OpenWindows [profile_window] >: EnableMenus [2,3] >: EnableMenuItems [11,12]; = (PState (ProfileInfo False formatted_profile formatted_module_profile total_profile) update_function print_setup,io); open_file_function file_name s=:(PState (ProfileInfo _ _ _ _) _ _) io = open_file_function file_name <:: file_close_function s io; sort_and_redraw_window compare_function (PState (ProfileInfo show_modules formatted_profile other_profile total_profile) update_function print_setup) io # formatted_profile=sortBy compare_function formatted_profile; = DrawInActiveWindowFrame redraw_window (PState (ProfileInfo show_modules formatted_profile other_profile total_profile) update_function print_setup) io; { redraw_window area s # (s,d)=update_function area s; = (s,[\picture->foldr EraseRectangle picture area:d]); } sort_by_time_function s io = sort_and_redraw_window ge_profile_time s io; sort_by_function_name s io = sort_and_redraw_window (\ p1 p2 -> p1.f_function_name<=p2.f_function_name) s io; sort_by_module_name s io = sort_and_redraw_window (\ p1 p2 -> p1.f_module_name<=p2.f_module_name) s io; sort_by_allocation_function s io = sort_and_redraw_window (\ p1 p2 -> p1.f_n_allocated_bytes>=p2.f_n_allocated_bytes) s io; sort_by_strict_function s io = sort_and_redraw_window (\ p1 p2 -> p1.f_n_strict_calls>=p2.f_n_strict_calls) s io; sort_by_lazy_function s io = sort_and_redraw_window (\ p1 p2 -> p1.f_n_lazy_calls>=p2.f_n_lazy_calls) s io; sort_by_curried_function s io = sort_and_redraw_window (\ p1 p2 -> p1.f_n_curried_calls>=p2.f_n_curried_calls) s io; show_functions :: !*MState *(IOState *MState) -> *(!*MState,!*IOState *MState); show_functions s=:(PState (ProfileInfo True _ _ _) update_function print_setup) io = show_other_profile s io; show_functions s io = (s,io); show_modules :: !*MState *(IOState *MState) -> *(!*MState,!*IOState *MState); show_modules s=:(PState (ProfileInfo False _ _ _) update_function print_setup) io = show_other_profile s io; show_modules s io = (s,io); show_other_profile (PState (ProfileInfo show_modules formatted_profile other_profile total_profile) update_function print_setup) io # (ascent,descent,_,_)=FontMetrics monaco9_font; window_height=window_height_function ascent descent other_profile; s=PState (ProfileInfo (not show_modules) other_profile formatted_profile total_profile) update_function print_setup; (s,io) = ChangePictureDomain 0 ((0,0),(WindowWidth,window_height)) s io; = DrawInActiveWindowFrame redraw_window s io; { redraw_window area s # (s,d)=update_function area s; = (s,[\picture->foldr EraseRectangle picture area:d]); } show_help s io = (s, ShowHelp HelpFileName io); import deltaIOState,deltaDialog; import deltaPrint; page_setup (PState pi update_function print_setup) io # (print_setup,io) = case print_setup of { PrintNotSetup -> defaultPrintSetup io; MPrintSetup print_setup -> (print_setup,io); }; # (print_setup,io) = printSetupDialog print_setup io; = ((PState pi update_function (MPrintSetup print_setup)),io); printTable :: *MState *(IOState *MState) -> *(*MState,*IOState *MState); printTable (PState pi=:(ProfileInfo _ functionData _ sumData) update_function print_setup) io # (print_setup,io) = case print_setup of { PrintNotSetup -> defaultPrintSetup io; MPrintSetup print_setup -> (print_setup,io); }; # s=PState pi update_function (MPrintSetup print_setup); # (doesntFit,{s,io}) = print2 True True generate_pages print_setup {s=s, io=io}; | doesntFit # (_,s,io) = OpenNotice (Notice ["The paper is not wide enough to","print all columns.", "Try landscape format."] (NoticeButton 0 "Ok") []) s io; = (s,io); = (s,io); {}{ column_l_positions={LPrinterPos0,LPrinterPos1,LPrinterPos2,LPrinterPos3,LPrinterPos4,LPrinterPos5,LPrinterPos6,LPrinterPos7,LPrinterPos8}; column_p_positions={PPrinterPos0,PPrinterPos1,PPrinterPos2,PPrinterPos3,PPrinterPos4,PPrinterPos5,PPrinterPos6,PPrinterPos7,PPrinterPos8}; generate_pages {printSetup,jobInfo={range=(first,last),copies}} picture # {page=page=:(maxX,maxY),resolution=(horizontal_dpi,_)} = getPageDimensions printSetup True; # landscape = maxX > maxY; # printFont = if landscape geneva8_font geneva6_font; # (ascent,descent,maxW,_)=FontMetrics printFont; # line_height=ascent+descent+1; # nrLinesPerPage = (maxY+1)/line_height; pages_without_sum = groupBy (nrLinesPerPage-2) functionData; printed_pages = pages_without_sum % (first-1,last-1); | isEmpty printed_pages = ([],picture,False); // ensure, that sum is always printed, regardless of the range of pages the user has choosen # (all_but_last,last_page) = splitAt ((length printed_pages)-1) printed_pages; last_page_1 = (hd last_page) % (0,nrLinesPerPage-3); // there needs to be place for the sum line new_last_page = last_page_1 ++ [sumData]; pages = all_but_last ++ [new_last_page]; // ok # column_positions=if landscape column_l_positions column_p_positions; # printer_window_width = if landscape LPrinterWindowWidth PPrinterWindowWidth; # char_width = if landscape 5 3; # printer_window_width=PCorMac (printer_window_width*horizontal_dpi/72) printer_window_width; # column_positions = PCorMac {pos*horizontal_dpi/72 \\ pos<-: column_positions} column_positions; # char_width = PCorMac (char_width*horizontal_dpi/72) char_width; # all_drawfuncs = map (\lines_in_one_page -> draw_table lines_in_one_page [((0,0),page)] column_positions printer_window_width char_width printFont) pages; = (flatten (repeatn copies all_drawfuncs),picture,False); }; print2 :: Bool Bool (PrintInfo *Picture -> *([*Picture -> *Picture],*Picture,Bool)) PrintSetup *(PState MState) -> *(!Bool,!*(PState MState)); print2 doDialog emulateScreen generate_pages print_setup ps=:{s,io} # (alt,printEnv) = printPagePerPage doDialog emulateScreen 0 initFun stateTransition print_setup //{state,io} {ps & io=io}; = case alt of { StartedPrinting (_,doesntFit,print_setup) # {s=(PState pi update_function _),io}=printEnv; -> (doesntFit,{s=PState pi update_function (MPrintSetup print_setup),io=io}); Cancelled _ -> (False,printEnv); }; { initFun _ printInfo=:{printSetup} picture # (drawFuns,picture,doesntFit) = generate_pages printInfo picture; = ((isEmpty drawFuns,(0,0)), ((drawFuns,doesntFit,print_setup),picture)); stateTransition (([drawFun:rest],doesntFit,print_setup),picture) = ((isEmpty rest,(0,0)), ((rest,doesntFit,print_setup), drawFun picture)); }; groupBy :: !Int [x] -> [[x]]; groupBy n [] = []; groupBy n l = [take n l : groupBy n (drop n l)];