module database // ************************************************************************************************** // // A database application that can read, generate, browse and edit *.dbs files. // // The program has been written in Clean 1.3.2 and uses the Clean Standard Object I/O library 1.2 // // ************************************************************************************************** import StdEnv, StdIO, Notice from StdFunc import seq,seqList :: Record :== Entry // [Content] :: Descriptor :== Entry // [Fieldname] :: Entry = { maxwidth :: Int // Max string width , fields :: [String] // The entries } :: DataBase = { records :: [Record] // All records , descriptor :: Descriptor // All fieldnames , selection :: Int // Indicating current record selected , query :: [String] // Record to look for (its fields) , name :: String // Name of database , editinfoid :: Id // Id of info about use of edit dialog (query or record) , edittextids :: [Id] // Ids of the edit text fields of the edit dialog , separatorw :: Int // The width of the separator string , dbfont :: InfoFont // The database font information , dffont :: InfoFont // The dialog font information } :: InfoFont = { font :: Font // The font which is used , width :: Int // Its widest character , height :: Int // Its line height } instance zero Entry where zero = {maxwidth=0,fields=[""]} CharsInInputBox :== 20 // Input width (number of characters) Replace :== True // Replace current selection when adding new record Separator :== ": " // Separates field names and contents CurrentRecordNr :== "Current Record Nr: " // The text field that displays the current record nr Start :: *World -> *World Start world # ((dbInfo,dfInfo),world) = accScreenPicture getInfoFont world # (sw, world) = accScreenPicture (getFontStringWidth dbInfo.font Separator) world # (recordWindowId, world) = openId world // Id of window that shows the records # (edDialogId, world) = openId world // Id of window that edits a record # (fieldDialogId, world) = openId world // Id of window that edits the set up # (editInfoId, world) = openId world // Id of text that shows current record nr initState = { records = [zero] , descriptor = zero , selection = 0 , query = [""] , name = "Untitled" , editinfoid = editInfoId , edittextids = [] , separatorw = sw , dbfont = dbInfo , dffont = dfInfo } = StartDataBase (recordWindowId,edDialogId,fieldDialogId) initState world where getInfoFont :: *Picture -> ((InfoFont,InfoFont),*Picture) getInfoFont picture # ((_,dbFont),picture) = openFont {NonProportionalFontDef & fSize=9} picture # (dbMetrics, picture) = getFontMetrics dbFont picture # (dfFont, picture) = openDialogFont picture # (dfMetrics, picture) = getFontMetrics dfFont picture = ( ( { font=dbFont,width=dbMetrics.fMaxWidth,height=fontLineHeight dbMetrics } , { font=dfFont,width=dfMetrics.fMaxWidth,height=fontLineHeight dfMetrics } ) , picture ) StartDataBase :: (Id,Id,Id) DataBase *World -> *World StartDataBase ids=:(recordWindowId,edDialogId,fieldDialogId) initState world = startIO SDI // Show one database a time initState // The initial database state (seq [OpenMenu,ShowRecords,ShowEditDialog]) // The initialisation actions [ProcessOpenFiles OpenFiles,ProcessClose Quit] // The process attributes world where // The menu of the database: OpenMenu :: (PSt DataBase) -> PSt DataBase OpenMenu pState = snd (openMenu undef menu pState) where menu = Menu "&Commands" ( MenuItem "Show &Records" [MenuShortKey 'r', MenuFunction (noLS ShowRecords)] :+: MenuItem "&Edit..." [MenuShortKey 'e', MenuFunction (noLS ShowEditDialog)] :+: MenuItem "Change Set &Up..." [MenuShortKey 'u', MenuFunction (noLS ShowFieldDialog)] :+: MenuItem "Read &New..." [MenuShortKey 'o', MenuFunction (noLS (ReadNew Nothing))] :+: MenuItem "&Save As..." [MenuShortKey 's', MenuFunction (noLS SaveRecords)] :+: MenuSeparator [] :+: MenuItem "&Quit" [MenuShortKey 'q', MenuFunction (noLS Quit)] ) [] // The callback and initialisation functions of the menu: OpenFiles :: [String] (PSt DataBase) -> PSt DataBase OpenFiles dbnames database = ReadNew (if (isEmpty dbnames) Nothing (Just (hd dbnames))) database ReadNew :: (Maybe String) (PSt DataBase) -> PSt DataBase ReadNew maybe_dbname database = seq (map closeWindow [recordWindowId,edDialogId,fieldDialogId] ++ [ReadDataBase maybe_dbname,ShowRecords,ShowEditDialog]) database ReadDataBase :: (Maybe String) (PSt DataBase) -> PSt DataBase ReadDataBase maybe_dbname database=:{ls={dbfont={font}}} # (maybe_dbname,database) = case maybe_dbname of (Just _) -> (maybe_dbname,database) nothing -> selectInputFile database | isNothing maybe_dbname = appPIO beep database # dbname = fromJust maybe_dbname # (open,dbfile,database) = fopen dbname FReadText database | not open = appPIO beep database # (descr,dbfile) = FReadDescr dbfile # (recs, dbfile) = FReadRecords (length descr+1) dbfile // lines = length descr + empty line # recs = if (isEmpty recs) [repeatn (length descr) ""] recs # (close,database) = accFiles (fclose dbfile) database | not close = appPIO beep database | otherwise # (recs, database) = accPIO (accScreenPicture (GetRecordEntries font recs)) database # (descr,database) = accPIO (accScreenPicture (GetEntryFromFields font descr)) database = appPLoc (\db->{db & records = recs , descriptor= descr , query = repeatn (length descr.fields) "" , selection = 0 , name = dbname }) database where FReadDescr file # (nroffields,file) = FReadStrippedLine file # nroffields = toInt nroffields | nroffields==0 = ([""],file) | otherwise = seqList (repeatn nroffields FReadStrippedLine) file FReadRecords nroflines file | sfend file = ([], file) # ([_:record],file) = seqList (repeatn nroflines FReadStrippedLine) file # (records, file) = FReadRecords nroflines file | otherwise = ([record : records], file) FReadStrippedLine file # (line,file) = freadline file = (line%(0,size line - 2),file) // strip "\n" ShowRecords :: (PSt DataBase) -> PSt DataBase ShowRecords database=:{ls=state=:{records,descriptor,name,dbfont}} # domain = DbViewDomain state 0 (max (length records) 1) # (_,database) = openWindow undef (window domain) database = database where window domain = Window namewithoutdirectories NilLS [ WindowId recordWindowId , WindowPos (LeftTop,OffsetVector {vx=5,vy=5}) , WindowMouse (const True) Able (noLS1 MouseSelectItem) , WindowHScroll (stdScrollFunction Horizontal dbfont.width) , WindowVScroll (stdScrollFunction Vertical dbfont.height) , WindowViewDomain domain , WindowLook True (RecordWindowLook state) , WindowPen [PenBack (RGB {r=240,g=240,b=240})] ] namewithoutdirectories = toString (last (splitby dirseparator (fromString name))) ShowEditDialog :: (PSt DataBase) -> PSt DataBase ShowEditDialog database=:{ls=state=:{descriptor=descr,records=recs,selection,dffont,editinfoid}} # (ids,database) = accPIO (openIds nr_of_ids) database # database = {database & ls={state & edittextids=tl ids}} # (_,database) = openDialog undef (editDialog ids) database # database = SetTextFields infostring (if (isEmpty recs) [] (recs!!selection).fields) database = database where nr_descrs = length descr.fields nr_of_ids = nr_descrs + 1 // Generate Ids for descriptor fields and default button ("Add") inputboxwidth = CharsInInputBox*dffont.width infostring = CurrentRecordNr+++toString selection editDialog ids = Dialog "Edit Record" ( TextControl "" [ControlId editinfoid,ControlWidth (PixelWidth inputboxwidth)] :+: LayoutControl ( ListLS [ TextControl field [ControlPos (Left,NoOffset)] \\ field <- descr.fields ]) [ControlPos (Left,NoOffset)] :+: LayoutControl ( ListLS [ EditControl "" (PixelWidth inputboxwidth) 1 [ControlId (ids!!i),ControlPos (Left,NoOffset)] \\ i <- [1..nr_descrs] ]) [] :+: ButtonControl "Dis&plQ" [ ControlFunction (noLS DisplQuery),left,buttonwidth , ControlTip "Show current query fields." ] :+: ButtonControl "Set&Q" [ ControlFunction (noLS SetQuery),buttonwidth , ControlTip "Set current input fields as new query." ] :+: ButtonControl "Searc&hQ" [ ControlFunction (noLS Search),buttonwidth , ControlTip "Search next item matching query." ] :+: ButtonControl "Se&lectAllQ" [ ControlFunction (noLS SelectAll),buttonwidth , ControlTip "Set database content to all items matching query." ] :+: ButtonControl "&Replace" [ ControlFunction (noLS (AddRecord Replace)),left,buttonwidth , ControlTip "Replace current selection with input fields." ] :+: ButtonControl "&Delete" [ ControlFunction (noLS DeleteRecord),buttonwidth , ControlTip "Remove current selection from database." ] :+: ButtonControl "&Add" [ ControlFunction (noLS (AddRecord (not Replace))),ControlId addId,buttonwidth , ControlTip "Extend database with new item matching input fields." ] :+: ButtonControl "&Sort" [ ControlFunction (noLS Sort),buttonwidth , ControlTip "Sort database using first field." ] :+: ButtonControl "&Clear" [ ControlFunction (noLS Clear),left,buttonwidth , ControlTip "Clear all input fields." ] :+: ButtonControl "&Next/Prev" [ ControlModsFunction (noLS1 NextPrev),buttonwidth , ControlTip "Select next or previous (press shift) database item." ] ) [ WindowId edDialogId , WindowOk addId , WindowClose (noLS (closeWindow edDialogId)) ] where buttonwidth = ControlWidth (ContentWidth "SelectAllQ") left = ControlPos (Left,NoOffset) addId = hd ids ShowFieldDialog :: (PSt DataBase) -> PSt DataBase ShowFieldDialog database=:{ls=state=:{descriptor=d,dffont}} | isEmpty d.fields # inputboxwidth = CharsInInputBox*dffont.width = inputdialog "Give first field" inputboxwidth (\input->FieldChangeIO (add (-1) input)) database | otherwise # database = closeWindow edDialogId database # (ids,database)= accPIO (openIds 2) database # (_,database) = openDialog undef (fielddialog ids) database = database where fielddialog ids = Dialog "Change Set Up" ( TextControl "Select Field..." [] :+: RadioControl (radioitems d.fields) (Columns 1) 1 [ControlId selectId] :+: LayoutControl ( ButtonControl "&Delete" [ left,buttonwidth , ControlFunction (noLS (DeleteField fieldDialogId getselectedfield)) , ControlTip "Delete selected field." ] :+: ButtonControl "&Rename" [ left,buttonwidth , ControlFunction (noLS (RenameField fieldDialogId getselectedfield)) , ControlTip "Rename selected field." ] ) [left] :+: LayoutControl ( ButtonControl "&Move" [ left,buttonwidth , ControlFunction (noLS (MoveField fieldDialogId getselectedfield)) , ControlTip "Move selected field to other position." ] :+: ButtonControl "Add &New" [ left,buttonwidth , ControlId addId , ControlFunction (noLS (AddField fieldDialogId getselectedfield)) , ControlTip "Add new field to records." ] ) [] ) [ WindowId fieldDialogId , WindowOk addId , WindowClose (noLS (closeWindow fieldDialogId)) ] where left = ControlPos (Left,NoOffset) buttonwidth = ControlWidth (ContentWidth "Add New") getselectedfield dialoginfo = fromJust (snd (getRadioControlSelection selectId dialoginfo)) - 1 [addId,selectId:_] = ids SaveRecords :: (PSt DataBase) -> PSt DataBase SaveRecords database=:{ls=state=:{name,descriptor={fields=desc},records}} # (maybe_dbname,database) = selectOutputFile "Save As: " name database | isNothing maybe_dbname = database # dbname = fromJust maybe_dbname # (open,dbfile,database) = fopen dbname FWriteText database | not open = appPIO beep database # (close,database) = accFiles (fclose (seq (writedescriptor++writerecords) dbfile)) database | close = database | otherwise = appPIO beep database where writedescriptor = [fwritei (length desc), FWriteRecord desc] writerecords = [FWriteRecord rec.fields \\ rec <- records] FWriteRecord rec = fwrites (foldl (+++) "\n" (map (\field -> field +++ "\n") rec)) Quit :: (PSt DataBase) -> PSt DataBase Quit database = closeProcess (warn ["Save database before quitting?"] "&No" OK SaveRecords database) // Field set up changes FieldChangeIO :: (IdFun (PSt DataBase)) (PSt DataBase) -> PSt DataBase FieldChangeIO changefun database # database = changefun database # database = seq (map closeWindow [fieldDialogId,edDialogId]) database # database = UpdateDbDomain database = database AddField :: Id (WState -> Int) (PSt DataBase) -> PSt DataBase AddField windowid getfield database=:{ls=state=:{dffont}} # (maybe_wstate,database) = accPIO (getWindow windowid) database | isNothing maybe_wstate = appPIO beep database | otherwise # wstate = fromJust maybe_wstate fieldname = getfield wstate infotext = "Add after '"+++state.descriptor.fields!!fieldname+++"' new field" inputboxwidth = CharsInInputBox*dffont.width = inputdialog infotext inputboxwidth (\input->FieldChangeIO (add fieldname input)) database RenameField :: Id (WState -> Int) (PSt DataBase) -> PSt DataBase RenameField windowid getfield database=:{ls=state=:{dffont}} # (maybe_wstate,database) = accPIO (getWindow windowid) database | isNothing maybe_wstate = appPIO beep database | otherwise # wstate = fromJust maybe_wstate fieldtorename = getfield wstate infotext = "Rename '"+++state.descriptor.fields!!fieldtorename+++"' to" inputboxwidth = CharsInInputBox*dffont.width = inputdialog infotext inputboxwidth (\input->FieldChangeIO (rename fieldtorename input)) database MoveField :: Id (WState -> Int) (PSt DataBase) -> PSt DataBase MoveField windowid getfield database=:{ls={descriptor=d}} # (maybe_wstate,database) = accPIO (getWindow windowid) database | isNothing maybe_wstate = appPIO beep database | otherwise # wstate = fromJust maybe_wstate fieldtomove = getfield wstate (ids,database) = accPIO (openIds 2) database = snd (openDialog undef (movedialog ids fieldtomove) database) where movedialog ids fieldtomove = Dialog "Move Field" ( TextControl ("Move '" +++ d.fields!!fieldtomove +++ "' before: ") [] :+: RadioControl (radioitems (d.fields++[""])) (Rows (length d.fields+1)) 1 [ControlId selectId, ControlPos (Left,NoOffset)] :+: ButtonControl Cancel [ControlPos (Left,NoOffset),ControlFunction (noLS cancel)] :+: ButtonControl "Move" [ControlId okId, ControlFunction (noLS (ok (move fieldtomove)))] ) [ WindowOk okId ] where [okId, selectId:_] = ids ok mvf pState # (maybe_id,pState) = accPIO getActiveWindow pState | isNothing maybe_id = pState | otherwise # id = fromJust maybe_id # (Just dialoginfo,pState) = accPIO (getWindow id) pState destinationfield = fromJust (snd (getRadioControlSelection selectId dialoginfo))-1 = FieldChangeIO (mvf destinationfield) (closeWindow id pState) DeleteField :: Id (WState -> Int) (PSt DataBase) -> PSt DataBase DeleteField windowid getfield database # (maybe_wstate,database) = accPIO (getWindow windowid) database | isNothing maybe_wstate = appPIO beep database | otherwise # wstate = fromJust maybe_wstate = warn ["Are you sure?"] Cancel OK (FieldChangeIO (delete (getfield wstate))) database // Handling the edit dialog DisplQuery :: (PSt DataBase) -> PSt DataBase DisplQuery database=:{ls={descriptor,query}} = SetTextFields "Query :" query database SetQuery :: (PSt DataBase) -> PSt DataBase SetQuery database=:{ls=state} # (nquery,database) = GetTextFields database = {database & ls={state & query = nquery.fields}} Search :: (PSt DataBase) -> PSt DataBase Search database=:{ls=state=:{records,query,selection=sel,edittextids},io} | isEmpty found = appPIO beep database | otherwise # database = ChangeSelection edittextids sel nsel {database & ls = {state & selection=nsel}} = MakeSelectionVisible database where nsel = hd found found = [i \\ e <- el ++ bl & i <- [sel+1 .. length records - 1] ++ [0..] | QueryRecord query e] (bl,el) = splitAt (sel+1) records QueryRecord :: [String] Record -> Bool QueryRecord query {fields} = and [ EqPref qf f \\ f <- fields & qf <- query ] where EqPref pref name | size_pref > size_name = False | otherwise = pref == name%(0,size_pref - 1) || EqPref pref (name%(1,size_name - 1)) where size_pref = size pref size_name = size name SelectAll :: (PSt DataBase) -> PSt DataBase SelectAll database=:{ls=state=:{records,query,selection,descriptor,dbfont,edittextids}} # recs = filter (QueryRecord query) records | isEmpty recs = appPIO beep database | otherwise # database = appPIO (setWindowTitle recordWindowId "Select") database state = {state & selection=0,records=recs,name="Select"} # database = {database & ls=state} # database = ChangeSelection edittextids selection 0 database # database = UpdateDbDomain database = database MakeSelectionVisible :: (PSt DataBase) -> PSt DataBase MakeSelectionVisible database=:{ls=state=:{records,selection,descriptor,dbfont},io} | isEmpty records = database # (viewframe,io) = getWindowViewFrame recordWindowId io visibletop = viewframe.corner1.y visiblebot = viewframe.corner2.y | selthumb >= visibletop && selthumb < visiblebot // selection visible = {database & io=io} | otherwise // selection invisible = {database & io=moveWindowViewFrame recordWindowId {vx=0,vy=selthumb-visibletop} io} where selthumb = toPicCo dbfont descriptor selection DeleteRecord :: (PSt DataBase) -> PSt DataBase DeleteRecord database=:{ls=state=:{records=oldrecs,selection=oldindex}} | isEmpty oldrecs = appPIO beep database | otherwise = UpdateDbDomain {database & ls={state & records = newrecs, selection = newindex}} where newrecs = removeAt oldindex oldrecs newindex= if (isEmpty newrecs) 0 (oldindex rem length newrecs) AddRecord :: Bool (PSt DataBase) -> PSt DataBase AddRecord replace database=:{ls=state=:{selection,records=recs,dbfont},io} | isEmpty recs && replace = appPIO beep database | otherwise # (newrec,database) = GetTextFields database (index,newrecs) = insertindex (<=) newrec (if replace (removeAt selection recs) recs) nstate = {state & records=newrecs,selection=index} = UpdateDbDomain {database & ls=nstate} Sort :: (PSt DataBase) -> PSt DataBase Sort database=:{ls=state=:{records=recs}} = UpdateDbDomain {database & ls={state & records = sort recs}} Clear :: (PSt DataBase) -> PSt DataBase Clear database=:{ls={editinfoid,edittextids}} = appPIO (setControlTexts [(id,"") \\ id<-edittextids]) database NextPrev :: Modifiers (PSt DataBase) -> PSt DataBase NextPrev {shiftDown} database=:{ls=state=:{records,selection=sel,edittextids}} # database = ChangeSelection edittextids sel nsel {database & ls = {state & selection=nsel}} = MakeSelectionVisible database where max_i = length records - 1 nsel = if shiftDown (if (sel==0) max_i (sel-1)) (if (sel==max_i) 0 (sel+1)) GetTextFields :: (PSt DataBase) -> (Entry,PSt DataBase) GetTextFields database=:{ls={edittextids,dbfont}} # (Just dialog,database) = accPIO (getWindow edDialogId) database fields = [text \\ (_,Just text) <- getControlTexts edittextids dialog] # (widths,database) = accPIO (accScreenPicture (getFontStringWidths dbfont.font fields)) database = ({maxwidth=maxList widths,fields=fields},database) SetTextFields :: String [String] (PSt DataBase) -> PSt DataBase SetTextFields s rec database=:{ls={editinfoid,edittextids}} = appPIO (setControlTexts (zip2 [editinfoid:edittextids] [s:rec])) database // Handling mouse clicks in database window MouseSelectItem :: MouseState (PSt DataBase) -> PSt DataBase MouseSelectItem (MouseDown {y} _ _) database=:{ls=state=:{records,descriptor,selection,dbfont,edittextids},io} | isEmpty records = database | otherwise # (Just viewDomain,database) = accPIO (getWindowViewDomain recordWindowId) database # index = toRecCo dbfont descriptor (min y (viewDomain.corner2.y-1)) # nstate = {state & selection=index} = ChangeSelection edittextids selection index {database & ls=nstate} MouseSelectItem _ database = database // Update the whole window in case the ViewDomain has changed UpdateDbDomain :: (PSt DataBase) -> PSt DataBase UpdateDbDomain database=:{ls=state} # viewdomain= DbViewDomain state 0 (max (length state.records) 1) # database = appPIO (setWindowLook recordWindowId True (True,RecordWindowLook state)) database # database = appPIO (setWindowViewDomain recordWindowId viewdomain) database # database = MakeSelectionVisible database = database ChangeSelection :: [Id] Int Int (PSt DataBase) -> PSt DataBase ChangeSelection edittextids old new database=:{ls=state=:{descriptor=descr,records},io} # io = appWindowPicture recordWindowId (HiliteSelection state new o (HiliteSelection state old)) io # io = setWindowLook recordWindowId False (True,RecordWindowLook state) io = SetTextFields infostring (records!!new).fields {database & io=io} where infostring = CurrentRecordNr+++toString new // Functions that change the content of particular fields add afterfield fieldname database=:{ls=state=:{records=rs,descriptor=d,query=q,dbfont}} # (w,database)= accPIO (accScreenPicture (getFontStringWidth dbfont.font fieldname)) database = {database & ls={state & records = map (\r->{r & fields=ins afterfield "" r.fields}) rs , descriptor = {d & fields=ins afterfield fieldname d.fields,maxwidth=max w d.maxwidth} , query = ins afterfield "" q } } ins afterfield x ys = insertAt (afterfield+1) x ys rename selectedfield newfieldname database=:{ls=state=:{descriptor=d,dbfont}} # (w,database)= accPIO (accScreenPicture (getFontStringWidth dbfont.font newfieldname)) database = {database & ls={state & descriptor={d & maxwidth=max w d.maxwidth,fields=newfields}}} where newfields = updateAt selectedfield newfieldname d.fields move sf df database=:{ls=state=:{records=rs,descriptor=d,query=q}} = {database & ls={state & records = map (\r=:{fields}->{r & fields=moveinlist sf df fields}) rs , descriptor = {d & fields=moveinlist sf df d.fields} , query = moveinlist sf df q } } delete i database=:{ls=state=:{records,descriptor={fields=d},query=q,dbfont}} # (recs,database) = accPIO (accScreenPicture (GetRecordEntries dbfont.font [removeAt i r.fields \\ r<-records])) database # (desc,database) = accPIO (accScreenPicture (GetEntryFromFields dbfont.font (removeAt i d))) database = {database & ls={state & records = recs , descriptor = desc , query = removeAt i q } } // Drawing utilities DbViewDomain :: DataBase Int Int -> ViewDomain DbViewDomain {descriptor,records,dbfont,separatorw} fr to = { corner1 = {x = ~whiteMargin ,y = toPicCo dbfont descriptor fr } , corner2 = {x = dw + separatorw + fw + whiteMargin,y = toPicCo dbfont descriptor to } } where whiteMargin = dbfont.width dw = descriptor.maxwidth fw = maxList [rec.maxwidth \\ rec<-records] RecordWindowLook :: DataBase SelectState UpdateState *Picture -> *Picture RecordWindowLook state _ {updArea=domains} picture = appClipPicture (toRegion domains) (RecordWindowLook` state domains) (seq (map unfill domains) picture) where RecordWindowLook` :: DataBase [Rectangle] *Picture -> *Picture RecordWindowLook` state=:{records=recs,descriptor=descr,selection,dbfont} domains picture # picture = setPenFont dbfont.font picture # picture = seq (map Update domains) picture # picture = HiliteSelection state selection picture = picture where Update :: Rectangle *Picture -> *Picture Update domain=:{corner1={y=top},corner2={y=bottom}} picture // #picture = unfill domain picture | isEmpty recs = picture | otherwise # picture = setPenPos {x=0,y=topofvisiblerecs} picture # picture = seq (map (DrawRec descr) (recs%(toprec,botrec))) picture = picture where topofvisiblerecs= toPicCo dbfont descr toprec toprec = toRecCo dbfont descr top botrec = toRecCo dbfont descr (bottom-1) DrawRec :: Descriptor Record *Picture -> *Picture DrawRec {fields=descr} {fields=rec} picture # picture = drawLine "" picture # picture = seq [drawLine (d +++ Separator +++ f) \\ d<-normwidth descr & f<-rec] picture = picture where normwidth descr = [f +++ toString (spaces ((maxList (map (size ) descr)) - size f)) \\ f <- descr] drawLine s picture # (curPenPos,picture) = getPenPos picture # picture = draw s picture # picture = setPenPos {curPenPos & y=curPenPos.y+dbfont.height} picture = picture HiliteSelection :: DataBase Int *Picture -> *Picture HiliteSelection s i pict = hilite (DbViewDomain s i (i+1)) pict // Switching between picture coordinates and indices in the list of records ('record coordinates') toPicCo:: InfoFont Descriptor Int -> Int toPicCo dbfont descr n = n * (inc (length descr.fields) * dbfont.height) toRecCo:: InfoFont Descriptor Int -> Int toRecCo dbfont descr n = n / (inc (length descr.fields) * dbfont.height) // Various useful functions instance < Size where (<) :: !Size !Size -> Bool (<) {w=w1,h=h1} {w=w2,h=h2} = w1 Bool (<) {fields=f1} {fields=f2} = f1 (Entry,*Picture) GetEntryFromFields font fields picture # (widths,picture) = getFontStringWidths font fields picture = ({maxwidth=maxList widths,fields=fields},picture) GetRecordEntries :: Font [[String]] *Picture -> ([Entry],*Picture) GetRecordEntries font recs picture # picture = setPenFont font picture # (widths,picture) = seqList (map getPenFontStringWidths recs) picture = ([{maxwidth=maxList ws,fields=fs} \\ ws<-widths & fs<-recs],picture) // Functions that should be library functions Cancel :== "&Cancel" OK :== "&OK" inputdialog name width fun pState # (ids,pState) = accPIO (openIds 2) pState # (_,pState) = openDialog undef (dialogdef ids) pState = pState where dialogdef ids = Dialog name ( TextControl (name+++": ") [] :+: EditControl "" (PixelWidth width) 1 [ControlId inputId] :+: ButtonControl Cancel [ControlPos (BelowPrev,NoOffset),ControlFunction (noLS cancel)] :+: ButtonControl OK [ControlId okId, ControlFunction (noLS (ok fun))] ) [ WindowOk okId ] where [inputId,okId:_] = ids ok fun pState # (Just id, pState) = accPIO (getParentId inputId) pState # (Just dialog,pState) = accPIO (getWindow id) pState input = fromJust (snd (getControlText inputId dialog)) = fun input (closeWindow id pState) warn info canceltext oktext fun pState = openNotice (Notice info (NoticeButton oktext (noLS (fun o cancel))) [NoticeButton canceltext id]) pState cancel pState # (maybeId,pState) = accPIO getActiveWindow pState | isNothing maybeId = pState # id = fromJust maybeId = closeWindow id pState insertindex :: !(a -> a -> Bool) !a !u:[a] -> (!Int,!v:[a]), [u <= v] insertindex r x ls = inserti r 0 x ls where inserti :: !(a -> .(a -> .Bool)) !Int !a !u:[a] -> (!Int,!v:[a]), [u <= v] inserti r i x ls=:[y:ys] | r x y = (i,[x:ls]) | otherwise # (index,list) = inserti r (inc i) x ys = (index,[y:list]) inserti _ i x _ = (i,[x]) moveinlist :: !Int !Int !.[a] -> [a] moveinlist src dest l // should be in StdList | src < dest = removeAt src beforedest ++ [l!!src : atdest] | src > dest = beforedest ++ [l!!src : removeAt (src - dest) atdest] | otherwise = l where (beforedest,atdest) = splitAt dest l splitby :: a !.[a] -> [.[a]] | Eq a splitby x ys = case rest of [] -> [firstpart]; [r:rs] -> [firstpart:splitby x rs] where (firstpart,rest) = span ((<>) x) ys