root/packages/defconAppKit/trunk/Lib/defconAppKit/controls/fontInfoView.py

Revision 686, 62.0 kB (checked in by tal, 8 months ago)
A hack around NSNumberFormatter not allowing "-" to be entered by the user.
Line 
1 import time
2 from Foundation import *
3 from AppKit import *
4 import vanilla
5 from ufo2fdk.fontInfoData import getAttrWithFallback, dateStringToTimeValue
6 from defconAppKit.tools.roundedRectBezierPath import roundedRectBezierPath
7
8 import objc
9 objc.setVerbose(True)
10
11
12 # -----------------------------------
13 # Formatters
14 # These will be used in the controls.
15 # -----------------------------------
16
17 integerFormatter = NSNumberFormatter.alloc().init()
18 integerFormatter.setFormat_("#;0;-#")
19 integerFormatter.setAllowsFloats_(False)
20 integerFormatter.setGeneratesDecimalNumbers_(False)
21
22 integerPositiveFormatter = NSNumberFormatter.alloc().init()
23 integerPositiveFormatter.setFormat_("#;0;-#")
24 integerPositiveFormatter.setAllowsFloats_(False)
25 integerPositiveFormatter.setGeneratesDecimalNumbers_(False)
26 integerPositiveFormatter.setMinimum_(0)
27
28 floatFormatter = NSNumberFormatter.alloc().init()
29 floatFormatter.setNumberStyle_(NSNumberFormatterDecimalStyle)
30 floatFormatter.setFormat_("#.00;0.00;-#.00")
31 floatFormatter.setAllowsFloats_(True)
32 floatFormatter.setGeneratesDecimalNumbers_(False)
33
34 positiveFloatFormatter = NSNumberFormatter.alloc().init()
35 positiveFloatFormatter.setAllowsFloats_(True)
36 positiveFloatFormatter.setGeneratesDecimalNumbers_(False)
37 positiveFloatFormatter.setMinimum_(0)
38
39 class NegativeIntegerEditText(vanilla.EditText):
40
41     def __init__(self, *args, **kwargs):
42         self._finalCallback = kwargs.get("callback")
43         kwargs["callback"] = self._textEditCallback
44         super(NegativeIntegerEditText, self).__init__(*args, **kwargs)
45
46     def _breakCycles(self):
47         self._finalCallback = None
48         super(NegativeIntegerEditText, self)._breakCycles()
49
50     def _get(self):
51         return self._nsObject.stringValue()
52
53     def get(self):
54         v = self._get()
55         if not v:
56             return None
57         v = int(v)
58         return v
59
60     def _textEditCallback(self, sender):
61         value = sender._get()
62         if value != "-":
63             try:
64                 v = int(value)
65                 if v > 0:
66                     sender.set("")
67                     return
68             except ValueError:
69                 if value.startswith("-"):
70                     value = value = "-"
71                 else:
72                     value = ""
73                 sender.set(value)
74                 return
75             if self._finalCallback is not None:
76                 self._finalCallback(sender)
77
78
79 class PostscriptStemSnapFormatter(NSFormatter):
80
81     def stringForObjectValue_(self, obj):
82         if obj is None or isinstance(obj, NSNull):
83             return ""
84         return " ".join([str(i) for i in obj])
85
86     def getObjectValue_forString_errorDescription_(self, value, string, error):
87         if not string.strip():
88             return True, [], ""
89         try:
90             values = [int(i) for i in string.strip().split(" ")]
91         except ValueError:
92             return False, string, "Could not convert entries to integers."
93         if len(values) >= 12:
94             return False, string, "Too many values."
95         return True, values, ""
96
97
98 class PostscriptBluesFormatter(NSFormatter):
99
100     def stringForObjectValue_(self, obj):
101         if obj is None or isinstance(obj, NSNull):
102             return ""
103         return " ".join([str(i) for i in obj])
104
105     def getObjectValue_forString_errorDescription_(self, value, string, error):
106         if not string.strip():
107             return True, [], ""
108         try:
109             values = [int(i) for i in string.strip().split(" ")]
110             values = sorted(values)
111         except ValueError:
112             return False, string, "Could not convert entries to integers."
113         if len(values) % 2:
114             return False, string, "An even number of values is required."
115         if len(values) >= 14:
116             return False, string, "Too many values."
117         return True, values, ""
118
119
120 # --------------------------------------------------------
121 # Special Controls
122 # These are vanilla subclasses that have special behavior.
123 # --------------------------------------------------------
124
125 panoseFamilyKindOptions = """Any
126 No Fit
127 Latin Text
128 Latin Hand Written
129 Latin Decorative
130 Latin Symbol""".splitlines()
131
132 panoseLatinTextOptions = """
133 Serif Style
134 Weight
135 Proportion
136 Contrast
137 Stroke Variation
138 Arm Style
139 Letterform
140 Midline
141 X-height
142 ---
143 Any
144 No Fit
145 Cove
146 Obtuse Cove
147 Square Cove
148 Obtuse Square Cove
149 Square
150 Thin
151 Oval
152 Exaggerated
153 Triangle
154 Normal Sans
155 Obtuse Sans
156 Perpendicular Sans
157 Flared
158 Rounded
159 ---
160 Any
161 No Fit
162 Very Light
163 Light
164 Thin
165 Book
166 Medium
167 Demi
168 Bold
169 Heavy
170 Black
171 Extra Black
172 ---
173 Any
174 No fit
175 Old Style
176 Modern
177 Even Width
178 Extended
179 Condensed
180 Very Extended
181 Very Condensed
182 Monospaced
183 ---
184 Any
185 No Fit
186 None
187 Very Low
188 Low
189 Medium Low
190 Medium
191 Medium High
192 High
193 Very High
194 ---
195 Any
196 No Fit
197 No Variation
198 Gradual/Diagonal
199 Gradual/Transitional
200 Gradual/Vertical
201 Gradual/Horizontal
202 Rapid/Vertical
203 Rapid/Horizontal
204 Instant/Vertical
205 Instant/Horizontal
206 ---
207 Any
208 No Fit
209 Straight Arms/Horizontal
210 Straight Arms/Wedge
211 Straight Arms/Vertical
212 Straight Arms/Single Serif
213 Straight Arms/Double Serif
214 Non-Straight/Horizontal
215 Non-Straight/Wedge
216 Non-Straight/Vertical
217 Non-Straight/Single Serif
218 Non-Straight/Double Serif
219 ---
220 Any
221 No Fit
222 Normal/Contact
223 Normal/Weighted
224 Normal/Boxed
225 Normal/Flattened
226 Normal/Rounded
227 Normal/Off Center
228 Normal/Square
229 Oblique/Contact
230 Oblique/Weighted
231 Oblique/Boxed
232 Oblique/Flattened
233 Oblique/Rounded
234 Oblique/Off Center
235 Oblique/Square
236 ---
237 Any
238 No Fit
239 Standard/Trimmed
240 Standard/Pointed
241 Standard/Serifed
242 High/Trimmed
243 High/Pointed
244 High/Serifed
245 Constant/Trimmed
246 Constant/Pointed
247 Constant/Serifed
248 Low/Trimmed
249 Low/Pointed
250 Low/Serifed
251 ---
252 Any
253 No Fit
254 Constant/Small
255 Constant/Standard
256 Constant/Large
257 Ducking/Small
258 Ducking/Standard
259 Ducking/Large
260 """
261
262 panoseLatinHandWrittenOptions = """
263 Tool Kind
264 Weight
265 Spacing
266 Aspect Ratio
267 Contrast
268 Topology
269 Form
270 Finials
271 X-ascent
272 ---
273 Any
274 No Fit
275 Flat Nib
276 Pressure Point
277 Engraved
278 Ball (Round Cap)
279 Brush
280 Rough
281 Felt Pen/Brush Tip
282 Wild Brush - Drips a lot
283 ---
284 Any
285 No Fit
286 Very Light
287 Light
288 Thin
289 Book
290 Medium
291 Demi
292 Bold
293 Heavy
294 Black
295 Extra Black (Nord)
296 ---
297 Any
298 No fit
299 Proportional Spaced
300 Monospaced
301 ---
302 Any
303 No Fit
304 Very Condensed
305 Condensed
306 Normal
307 Expanded
308 Very Expanded
309 ---
310 Any
311 No Fit
312 None
313 Very Low
314 Low
315 Medium Low
316 Medium
317 Medium High
318 High
319 Very High
320 ---
321 Any
322 No Fit
323 Roman Disconnected
324 Roman Trailing
325 Roman Connected
326 Cursive Disconnected
327 Cursive Trailing
328 Cursive Connected
329 Blackletter Disconnected
330 Blackletter Trailing
331 Blackletter Connected
332 ---
333 Any
334 No Fit
335 Upright / No Wrapping
336 Upright / Some Wrapping
337 Upright / More Wrapping
338 Upright / Extreme Wrapping
339 Oblique / No Wrapping
340 Oblique / Some Wrapping
341 Oblique / More Wrapping
342 Oblique / Extreme Wrapping
343 Exaggerated / No Wrapping
344 Exaggerated / Some Wrapping
345 Exaggerated / More Wrapping
346 Exaggerated / Extreme Wrapping
347 ---
348 Any
349 No Fit
350 None / No loops
351 None / Closed loops
352 None / Open loops
353 Sharp / No loops
354 Sharp / Closed loops
355 Sharp / Open loops
356 Tapered / No loops
357 Tapered / Closed loops
358 Tapered / Open loops
359 Round / No loops
360 Round / Closed loops
361 Round / Open loops
362 ---
363 Any
364 No Fit
365 Very Low
366 Low
367 Medium
368 High
369 Very High
370 """
371
372 panoseLatinDecorativesOptions = """
373 Class
374 Weight
375 Aspect
376 Contrast
377 Serif Variant
378 Treatment
379 Lining
380 Topology
381 Range of Characters
382 ---
383 Any
384 No Fit
385 Derivative
386 Non-standard Topology
387 Non-standard Elements
388 Non-standard Aspect
389 Initials
390 Cartoon
391 Picture Stems
392 Ornamented
393 Text and Background
394 Collage
395 Montage
396 ---
397 Any
398 No Fit
399 Very Light
400 Light
401 Thin
402 Book
403 Medium
404 Demi
405 Bold
406 Heavy
407 Black
408 Extra Black
409 ---
410 Any
411 No fit
412 Super Condensed
413 Very Condensed
414 Condensed
415 Normal
416 Extended
417 Very Extended
418 Super Extended
419 Monospaced
420 ---
421 Any
422 No Fit
423 None
424 Very Low
425 Low
426 Medium Low
427 Medium
428 Medium High
429 High
430 Very High
431 Horizontal Low
432 Horizontal Medium
433 Horizontal High
434 Broken
435 ---
436 Any
437 No Fit
438 Cove
439 Obtuse Cove
440 Square Cove
441 Obtuse Square Cove
442 Square
443 Thin
444 Oval
445 Exaggerated
446 Triangle
447 Normal Sans
448 Obtuse Sans
449 Perpendicular Sans
450 Flared
451 Rounded
452 Script
453 ---
454 Any
455 No Fit
456 None - Standard Solid Fill
457 White / No Fill
458 Patterned Fill
459 Complex Fill
460 Shaped Fill
461 Drawn / Distressed
462 ---
463 Any
464 No Fit
465 None
466 Inline
467 Outline
468 Engraved (Multiple Lines)
469 Shadow
470 Relief
471 Backdrop
472 ---
473 Any
474 No Fit
475 Standard
476 Square
477 Multiple Segment
478 Deco (E,M,S) Waco midlines
479 Uneven Weighting
480 Diverse Arms
481 Diverse Forms
482 Lombardic Forms
483 Upper Case in Lower Case
484 Implied Topology
485 Horseshoe E and A
486 Cursive
487 Blackletter
488 Swash Variance
489 ---
490 Any
491 No Fit
492 Extended Collection
493 Litterals
494 No Lower Case
495 Small Caps
496 """
497
498 panoseLatinPictorialOptions = """
499 Kind
500 Weight
501 Spacing
502 Aspect Ratio & Contrast
503 Aspect Ratio of Char. 94
504 Aspect Ratio of Char. 119
505 Aspect Ratio of Char. 157
506 Aspect Ratio of Char. 163
507 Aspect Ratio of Char. 211
508 ---
509 Any
510 No Fit
511 Montages
512 Pictures
513 Shapes
514 Scientific
515 Music
516 Expert
517 Patterns
518 Boarders
519 Icons
520 Logos
521 Industry specific
522 ---
523 Any
524 No Fit
525 ---
526 Any
527 No fit
528 Proportional Spaced
529 Monospaced
530 ---
531 Any
532 No Fit
533 ---
534 Any
535 No Fit
536 No Width
537 Exceptionally Wide
538 Super Wide
539 Very Wide
540 Wide
541 Normal
542 Narrow
543 Very Narrow
544 ---
545 Any
546 No Fit
547 No Width
548 Exceptionally Wide
549 Super Wide
550 Very Wide
551 Wide
552 Normal
553 Narrow
554 Very Narrow
555 ---
556 Any
557 No Fit
558 No Width
559 Exceptionally Wide
560 Super Wide
561 Very Wide
562 Wide
563 Normal
564 Narrow
565 Very Narrow
566 ---
567 Any
568 No Fit
569 No Width
570 Exceptionally Wide
571 Super Wide
572 Very Wide
573 Wide
574 Normal
575 Narrow
576 Very Narrow
577 ---
578 Any
579 No Fit
580 No Width
581 Exceptionally Wide
582 Super Wide
583 Very Wide
584 Wide
585 Normal
586 Narrow
587 Very Narrow
588 """
589
590 def makePanoseOptions(text):
591     text = text.strip()
592     groups = text.split("---")
593     groups = [i.strip() for i in groups]
594     assert len(groups) == 10
595     groups = [group.splitlines() for group in groups]
596     titles = groups[0]
597     options = groups[1:]
598     return titles, options
599
600 panoseControlOptionTree = [
601     ("Any", [[] for i in range(9)]),
602     ("No Fit", [[] for i in range(9)]),
603     makePanoseOptions(panoseLatinTextOptions),
604     makePanoseOptions(panoseLatinHandWrittenOptions),
605     makePanoseOptions(panoseLatinDecorativesOptions),
606     makePanoseOptions(panoseLatinPictorialOptions)
607 ]
608
609
610 class PanoseControl(vanilla.Group):
611
612     def __init__(self, posSize, titlePosition, titleWidth, buttonPosition, buttonWidth, callback):
613         super(PanoseControl, self).__init__(posSize)
614         self._callback = callback
615         self.title = vanilla.TextBox((titlePosition, 0, -0, 17), "Panose")
616         self.titleLine = vanilla.HorizontalLine((titlePosition, 22, -titlePosition, 1))
617         self.familyKindTitle = vanilla.TextBox((titlePosition, 42, titleWidth, 17), "Family Kind:", alignment="right")
618         self.familyKindPopUp = vanilla.PopUpButton((buttonPosition, 40, buttonWidth, 20), panoseFamilyKindOptions, self._familyKindCallback)
619         currentTop = 70
620         for i in range(9):
621             attribute = "title%d" % i
622             control = vanilla.TextBox((titlePosition, currentTop+2, titleWidth, 17), "", alignment="right")
623             setattr(self, attribute, control)
624             attribute = "popup%d" % i
625             control = vanilla.PopUpButton((buttonPosition, currentTop, buttonWidth, 20), [], callback=self._subdigitCallback)
626             setattr(self, attribute, control)
627             currentTop += 30
628         self._currentFamilyKind = 0
629
630     def _breakCycles(self):
631         self._callback = None
632         super(PanoseControl, self)._breakCycles()
633
634     def _familyKindCallback(self, sender):
635         value = sender.get()
636         if value == self._currentFamilyKind:
637             return
638         self.set([value, 0, 0, 0, 0, 0, 0, 0, 0, 0])
639         self._callback(self)
640
641     def _subdigitCallback(self, sender):
642         self._callback(self)
643
644     def set(self, value):
645         # get the family kind data
646         familyKind = self._currentFamilyKind = value[0]
647         familyTitles, familyOptions = panoseControlOptionTree[familyKind]
648         if familyKind in (0, 1):
649             familyTitles = "        ".split(" ")
650         # set the family
651         self.familyKindPopUp.set(familyKind)
652         # update the titles
653         for index, title in enumerate(familyTitles):
654             if title:
655                 title += ":"
656             attribute = "title%d" % index
657             control = getattr(self, attribute)
658             control.set(title)
659         # update the buttons
660         for index, options in enumerate(familyOptions):
661             attribute = "popup%d" % index
662             control = getattr(self, attribute)
663             control.setItems(options)
664             control.set(value[index+1])
665
666     def get(self):
667         familyKind = self.familyKindPopUp.get()
668         if familyKind in (0, 1):
669             values = [0, 0, 0, 0, 0, 0, 0, 0, 0]
670         else:
671             values = []
672             for index in range(9):
673                 attribute = "popup%d" % index
674                 control = getattr(self, attribute)
675                 values.append(control.get())
676         return [familyKind] + values
677
678
679 embeddingPopUpOptions = """
680 No embedding restrictions.
681 No embedding allowed.
682 Only preview and print embedding allowed.
683 Editable embedding allowed.
684 """.strip().splitlines()
685
686
687 class EmbeddingControl(vanilla.Group):
688
689     def __init__(self, posSize, callback):
690         super(EmbeddingControl, self).__init__(posSize)
691         self._callback = callback
692         self.basicsPopUp = vanilla.PopUpButton((0, 0, -0, 20), embeddingPopUpOptions, callback=self._controlCallback)
693         self.subsettingCheckBox = vanilla.CheckBox((0, 30, -0, 20), "Allow Subsetting", callback=self._controlCallback)
694         self.bitmapCheckBox = vanilla.CheckBox((0, 55, -10, 20), "Allow Only Bitmap Embedding", callback=self._controlCallback)
695         self.subsettingCheckBox.enable(False)
696         self.bitmapCheckBox.enable(False)
697
698     def _breakCycles(self):
699         self._callback = None
700         super(EmbeddingControl, self)._breakCycles()
701
702     def _controlCallback(self, sender):
703         self._handleEnable()
704         self._callback(self)
705
706     def _handleEnable(self):
707         enable = self.basicsPopUp.get() != 0
708         self.subsettingCheckBox.enable(enable)
709         self.bitmapCheckBox.enable(enable)
710
711     def set(self, values):
712         if 1 in values:
713             self.basicsPopUp.set(1)
714         elif 2 in values:
715             self.basicsPopUp.set(2)
716         elif 3 in values:
717             self.basicsPopUp.set(3)
718         else:
719             self.basicsPopUp.set(0)
720         self.subsettingCheckBox.set(not 8 in values)
721         self.bitmapCheckBox.set(9 in values)
722         self._handleEnable()
723
724     def get(self):
725         values = []
726         basicValue = self.basicsPopUp.get()
727         if basicValue != 0:
728             return values
729         if not self.subsettingCheckBox.get():
730             values.append(8)
731         if self.bitmapCheckBox.get():
732             values.append(9)
733         return values
734
735
736 class CheckList(vanilla.List):
737
738     def __init__(self, posSize, template, callback):
739         # create the dict items
740         self._bitToIndex = {}
741         self._indexToBit = {}
742         self._titles = list(template)
743         for index, title in enumerate(template):
744             bit = int(title.split(" ")[0])
745             self._bitToIndex[bit] = index
746             self._indexToBit[index] = bit
747         items = self._wrapItems()
748         # describe the columns
749         columnDescriptions = [
750             dict(title="value", cell=vanilla.CheckBoxListCell(), width=16),
751             dict(title="title"),
752         ]
753         # let super do the rest
754         super(CheckList, self).__init__(posSize, items, columnDescriptions=columnDescriptions,
755             showColumnTitles=False, autohidesScrollers=False, editCallback=callback, drawFocusRing=False)
756         self.getNSScrollView().setHasHorizontalScroller_(False)
757
758     def _wrapItems(self, selectedBits=[]):
759         items = []
760         for index, title in enumerate(self._titles):
761             bit = self._indexToBit[index]
762             d = dict(value=bit in selectedBits, title=title)
763             items.append(d)
764         return items
765
766     def set(self, items):
767         items = self._wrapItems(items)
768         super(CheckList, self).set(items)
769
770     def get(self):
771         items = super(CheckList, self).get()
772         bits = []
773         for index, item in enumerate(items):
774             if not item["value"]:
775                 continue
776             bit = self._indexToBit[index]
777             bits.append(bit)
778         return bits
779
780
781 # -------------------------------------------------------
782 # Input control definitions.
783 # These describe the controls and how they should behave.
784 # -------------------------------------------------------
785
786 def inputItemDict(**kwargs):
787     default = dict(
788         title=None,
789         hasDefault=True,
790         controlClass=vanilla.EditText,
791         #controlOptions=None,
792         conversionFromUFO=None,
793         conversionToUFO=None,
794     )
795     default.update(kwargs)
796     return default
797
798 def noneToZero(value):
799     print value
800     if value is None:
801         return 0
802     return value
803
804 ## Basic Naming
805
806 familyNameItem = inputItemDict(
807     title="Family Name",
808     hasDefault=False
809 )
810 styleNameItem = inputItemDict(
811     title="Style Name",
812     hasDefault=False
813 )
814 styleMapFamilyNameItem = inputItemDict(
815     title="Style Map Family Name"
816 )
817
818 styleMapStyleOptions = ["regular", "italic", "bold", "bold italic"]
819
820 def styleMapStyleNameFromUFO(value):
821     return styleMapStyleOptions.index(value)
822
823 def styleMapStyleNameToUFO(value):
824     return styleMapStyleOptions[value]
825
826 styleMapStyleNameItem = inputItemDict(
827     title="Style Map Style",
828     controlClass=vanilla.RadioGroup,
829     conversionFromUFO=styleMapStyleNameFromUFO,
830     conversionToUFO=styleMapStyleNameToUFO,
831     controlOptions=dict(items=["Regular", "Italic", "Bold", "Bold Italic"])
832 )
833 versionMajorItem = inputItemDict(
834     title="Version Major",
835     hasDefault=False,
836     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
837 )
838 versionMinorItem = inputItemDict(
839     title="Version Minor",
840     hasDefault=False,
841     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
842 )
843
844 ## Basic Dimensions
845
846 unitsPerEmItem = inputItemDict(
847     title="Units Per Em",
848     hasDefault=False,
849     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
850 )
851 descenderItem = inputItemDict(
852     title="Descender",
853     hasDefault=False,
854     controlClass=NegativeIntegerEditText,
855     controlOptions=dict(style="number"),
856     conversionToUFO=noneToZero
857 )
858 xHeightItem = inputItemDict(
859     title="x-height",
860     hasDefault=False,
861     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
862 )
863 capHeightItem = inputItemDict(
864     title="Cap-height",
865     hasDefault=False,
866     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
867 )
868 ascenderItem = inputItemDict(
869     title="Ascender",
870     hasDefault=False,
871     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
872 )
873 italicAngleItem = inputItemDict(
874     title="Italic Angle",
875     hasDefault=False,
876     controlOptions=dict(style="number", formatter=floatFormatter)
877 )
878
879 ## Basic Legal
880
881 copyrightItem = inputItemDict(
882     title="Copyright",
883     hasDefault=False,
884     controlOptions=dict(lineCount=5)
885 )
886 trademarkItem = inputItemDict(
887     title="Trademark",
888     hasDefault=False,
889     controlOptions=dict(lineCount=5)
890 )
891 openTypeNameLicenseItem = inputItemDict(
892     title="License",
893     hasDefault=False,
894     controlOptions=dict(lineCount=20)
895 )
896 openTypeNameLicenseURLItem = inputItemDict(
897     title="License URL",
898     hasDefault=False
899 )
900
901 ## Basic Parties
902
903 openTypeNameDesignerItem = inputItemDict(
904     title="Designer",
905     hasDefault=False
906 )
907 openTypeNameDesignerURLItem = inputItemDict(
908     title="Designer URL",
909     hasDefault=False
910 )
911 openTypeNameManufacturerItem = inputItemDict(
912     title="Manufacturer",
913     hasDefault=False,
914 )
915 openTypeNameManufacturerURLItem = inputItemDict(
916     title="Manufacturer URL",
917     hasDefault=False,
918 )
919
920 ## Basic Note
921
922 noteItem = inputItemDict(
923     title="",
924     hasDefault=False,
925     controlOptions=dict(lineCount=20)
926 )
927
928
929 ## OpenType head Table
930
931 def openTypeHeadCreatedFromUFO(value):
932     t = dateStringToTimeValue(value)
933     s = time.strftime("%Y/%m/%d %H:%M:%S +0000", time.gmtime(t))
934     return NSDate.dateWithString_(s)
935
936 def openTypeHeadCreatedToUFO(value):
937     value = value.descriptionWithCalendarFormat_timeZone_locale_("%Y/%m/%d %H:%M:%S", None, None)
938     return value
939
940 openTypeHeadCreatedItem = inputItemDict(
941     title="created",
942     controlClass=vanilla.DatePicker,
943     conversionFromUFO=openTypeHeadCreatedFromUFO,
944     conversionToUFO=openTypeHeadCreatedToUFO,
945 )
946
947 openTypeHeadLowestRecPPEMItem = inputItemDict(
948     title="lowestRecPPEM",
949     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
950 )
951
952 openTypeHeadFlagsOptions = [
953     "0 Baseline for font at y=0",
954     "1 Left sidebearing point at x=0",
955     "2 Instructions may depend on point size",
956     "3 Force ppem to integer values for all internal scaler math",
957     "4 Instructions may alter advance width",
958     "11 Font data is \"lossless\"",
959     "12 Font converted (produce compatible metrics)",
960     "13 Font optimized for ClearType",
961 ]
962
963 openTypeHeadFlagsItem = inputItemDict(
964     title="flags",
965     controlClass=CheckList,
966     controlOptions=dict(items=openTypeHeadFlagsOptions)
967 )
968
969 ## OpenType name Table
970
971 openTypeNamePreferredFamilyNameItem = inputItemDict(
972     title="Preferred Family Name"
973 )
974 openTypeNamePreferredSubfamilyNameItem = inputItemDict(
975     title="Preferred Subfamily Name"
976 )
977 openTypeNameCompatibleFullNameItem = inputItemDict(
978     title="Compatible Full Name"
979 )
980 openTypeNameWWSFamilyNameItem = inputItemDict(
981     title="WWS Family Name"
982 )
983 openTypeNameWWSSubfamilyNameItem = inputItemDict(
984     title="WWS Subfamily Name"
985 )
986 openTypeNameVersionItem = inputItemDict(
987     title="Version"
988 )
989 openTypeNameUniqueIDItem = inputItemDict(
990     title="Unique ID"
991 )
992 openTypeNameDescriptionItem = inputItemDict(
993     title="Description",
994     hasDefault=False,
995     controlOptions=dict(lineCount=5)
996 )
997 openTypeNameSampleTextItem = inputItemDict(
998     title="Sample Text",
999     hasDefault=False,
1000     controlOptions=dict(lineCount=5)
1001 )
1002
1003 ## OpenType hhea Table
1004
1005 openTypeHheaAscenderItem = inputItemDict(
1006     title="Ascender",
1007     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1008 )
1009 openTypeHheaDescenderItem = inputItemDict(
1010     title="Descender",
1011     controlClass=NegativeIntegerEditText,
1012     controlOptions=dict(style="number"),
1013     conversionToUFO=noneToZero
1014 )
1015 openTypeHheaLineGapItem = inputItemDict(
1016     title="LineGap",
1017     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1018 )
1019 openTypeHheaCaretSlopeRiseItem = inputItemDict(
1020     title="caretSlopeRise",
1021     controlOptions=dict(style="number", formatter=integerFormatter)
1022 )
1023 openTypeHheaCaretSlopeRunItem = inputItemDict(
1024     title="caretSlopeRun",
1025     controlOptions=dict(style="number", formatter=integerFormatter)
1026 )
1027 openTypeHheaCaretOffsetItem = inputItemDict(
1028     title="caretOffset",
1029     controlOptions=dict(style="number", formatter=integerFormatter)
1030 )
1031
1032 ## OpenType vhea Table
1033
1034 openTypeVheaVertTypoAscenderItem = inputItemDict(
1035     title="vertTypoAscender",
1036     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1037 )
1038 openTypeVheaVertTypoDescenderItem = inputItemDict(
1039     title="vertTypoDescender",
1040     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1041 )
1042 openTypeVheaVertTypoLineGapItem = inputItemDict(
1043     title="vertTypoLineGap",
1044     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1045 )
1046 openTypeVheaCaretSlopeRiseItem = inputItemDict(
1047     title="caretSlopeRise",
1048     controlOptions=dict(style="number", formatter=integerFormatter)
1049 )
1050 openTypeVheaCaretSlopeRunItem = inputItemDict(
1051     title="caretSlopeRun",
1052     controlOptions=dict(style="number", formatter=integerFormatter)
1053 )
1054 openTypeVheaCaretOffsetItem = inputItemDict(
1055     title="caretOffset",
1056     controlOptions=dict(style="number", formatter=integerFormatter)
1057 )
1058
1059 ## OpenType OS/2 Table
1060
1061 openTypeOS2WeightClassOptions = [
1062     "Ultra-condensed",
1063     "Extra-condensed",
1064     "Condensed",
1065     "Semi-condensed",
1066     "Medium (normal)",
1067     "Semi-expanded",
1068     "Expanded",
1069     "Extra-expanded",
1070     "Ultra-expanded"
1071 ]
1072
1073 def openTypeOS2WeightClassFromUFO(value):
1074     return value - 1
1075
1076 def openTypeOS2WeightClassToUFO(value):
1077     return value + 1
1078
1079 openTypeOS2WidthClassItem = inputItemDict(
1080     title="usWidthClass",
1081     hasDefault=False,
1082     controlClass=vanilla.PopUpButton,
1083     controlOptions=dict(items=openTypeOS2WeightClassOptions),
1084     conversionFromUFO=openTypeOS2WeightClassFromUFO,
1085     conversionToUFO=openTypeOS2WeightClassToUFO
1086 )
1087
1088 openTypeOS2WeightClassItem = inputItemDict(
1089     title="usWeightClass",
1090     hasDefault=False,
1091     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1092 )
1093
1094 openTypeOS2SelectionOptions = [
1095     "1 UNDERSCORE",
1096     "2 NEGATIVE",
1097     "3 OUTLINED",
1098     "4 STRIKEOUT",
1099     "7 USE_TYPO_METRICS",
1100     "8 WWS",
1101     "9 OBLIQUE",
1102 ]
1103
1104 openTypeOS2SelectionItem = inputItemDict(
1105     title="fsSelection",
1106     controlClass=CheckList,
1107     controlOptions=dict(items=openTypeOS2SelectionOptions)
1108 )
1109
1110 openTypeOS2VendorIDItem = inputItemDict(
1111     title="achVendID",
1112     hasDefault=False,
1113 )
1114 openTypeOS2PanoseItem = inputItemDict(
1115     title="",
1116     hasDefault=False,
1117     controlClass=PanoseControl,
1118     controlOptions=dict(formatter=PostscriptBluesFormatter.alloc().init())
1119 )
1120
1121 openTypeOS2UnicodeRangesOptions = [
1122     "0 Basic Latin",
1123     "1 Latin-1 Supplement",
1124     "2 Latin Extended-A",
1125     "3 Latin Extended-B",
1126     "4 IPA Extensions",
1127     "5 Spacing Modifier Letters",
1128     "6 Combining Diacritical Marks",
1129     "7 Greek and Coptic",
1130     "8 Coptic",
1131     "9 Cyrillic",
1132     "10 Armenian",
1133     "11 Hebrew",
1134     "12 Vai",
1135     "13 Arabic",
1136     "14 ",
1137     "15 Devanagari",
1138     "16 Bengali",
1139     "17 Gurmukhi",
1140     "18 Gujarati",
1141     "19 Oriya",
1142     "20 Tamil",
1143     "21 Telugu",
1144     "22 Kannada",
1145     "23 Malayalam",
1146     "24 Thai",
1147     "25 Lao",
1148     "26 Georgian",
1149     "27 Balinese",
1150     "28 Hangul Jamo",
1151     "29 Latin Extended Additional",
1152     "30 Greek Extended",
1153     "31 General Punctuation",
1154     "32 Superscripts And Subscripts",
1155     "33 Currency Symbols",
1156     "34 Combining Diacritical Marks For Symbols",
1157     "35 Letterlike Symbols",
1158     "36 Number Forms",
1159     "37 Arrows",
1160     "38 Mathematical Operators",
1161     "39 Miscellaneous Technical",
1162     "40 Control Pictures",
1163     "41 Optical Character Recognition",
1164     "42 Enclosed Alphanumerics",
1165     "43 Box Drawing",
1166     "44 Block Elements",
1167     "45 Geometric Shapes",
1168     "46 Miscellaneous Symbols",
1169     "47 Dingbats",
1170     "48 CJK Symbols And Punctuation",
1171     "49 Hiragana",
1172     "50 Katakana",
1173     "51 Bopomofo",
1174     "52 Hangul Compatibility Jamo",
1175     "53 Phags-pa",
1176     "54 Enclosed CJK Letters And Months",
1177     "55 CJK Compatibility",
1178     "56 Hangul Syllables",
1179     "57 Non-Plane 0 *",
1180     "58 Phoenician",
1181     "59 CJK Unified Ideographs",
1182     "60 Private Use Area (plane 0)",
1183     "61 CJK Strokes",
1184     "62 Alphabetic Presentation Forms",
1185     "63 Arabic Presentation Forms-A",
1186     "64 Combining Half Marks",
1187     "65 Vertical Forms",
1188     "66 Small Form Variants",
1189     "67 Arabic Presentation Forms-B",
1190     "68 Halfwidth And Fullwidth Forms",
1191     "69 Specials",
1192     "70 Tibetan",
1193     "71 Syriac",
1194     "72 Thaana",
1195     "73 Sinhala",
1196     "74 Myanmar",
1197     "75 Ethiopic",
1198     "76 Cherokee",
1199     "77 Unified Canadian Aboriginal Syllabics",
1200     "78 Ogham",
1201     "79 Runic",
1202     "80 Khmer",
1203     "81 Mongolian",
1204     "82 Braille Patterns",
1205     "83 Yi Syllables",
1206     "84 Tagalog",
1207     "85 Old Italic",
1208     "86 Gothic",
1209     "87 Deseret",
1210     "88 Byzantine Musical Symbols",
1211     "89 Mathematical Alphanumeric Symbols",
1212     "90 Private Use (plane 15)",
1213     "91 Variation Selectors",
1214     "92 Tags",
1215     "93 Limbu",
1216     "94 Tai Le",
1217     "95 New Tai Lue",
1218     "96 Buginese",
1219     "97 Glagolitic",
1220     "98 Tifinagh",
1221     "99 Yijing Hexagram Symbols",
1222     "100 Syloti Nagri",
1223     "101 Linear B Syllabary",
1224     "102 Ancient Greek Numbers",
1225     "103 Ugaritic",
1226     "104 Old Persian",
1227     "105 Shavian",
1228     "106 Osmanya",
1229     "107 Cypriot Syllabary",
1230     "108 Kharoshthi",
1231     "109 Tai Xuan Jing Symbols",
1232     "110 Cuneiform",
1233     "111 Counting Rod Numerals",
1234     "112 Sundanese",
1235     "113 Lepcha",
1236     "114 Ol Chiki",
1237     "115 Saurashtra",
1238     "116 Kayah Li",
1239     "117 Rejang",
1240     "118 Cham",
1241     "119 Ancient Symbols",
1242     "120 Phaistos Disc",
1243     "121 Carian",
1244     "122 Domino Tiles",
1245 ]
1246
1247 openTypeOS2UnicodeRangesItem = inputItemDict(
1248     title="ulUnicodeRange",
1249     hasDefault=False,
1250     controlClass=CheckList,
1251     controlOptions=dict(items=openTypeOS2UnicodeRangesOptions),
1252 )
1253
1254 openTypeOS2CodePageRangesOptions = [
1255     "0 1252 Latin 1",
1256     "1 1250 Latin 2: Eastern Europe",
1257     "2 1251 Cyrillic",
1258     "3 1253 Greek",
1259     "4 1254 Turkish",
1260     "5 1255 Hebrew",
1261     "6 1256 Arabic",
1262     "7 1257 Windows Baltic",
1263     "8 1258 Vietnamese",
1264     "16 874 Thai",
1265     "17 932 JIS/Japan",
1266     "18 936 Chinese: Simplified chars--PRC and Singapore",
1267     "19 949 Korean Wansung",
1268     "20 950 Chinese: Traditional chars--Taiwan and Hong Kong",
1269     "21 1361 Korean Johab",
1270     "29 Macintosh Character Set (US Roman)",
1271     "30 OEM Character Set",
1272     "31 Symbol Character Set",
1273     "48 869 IBM Greek",
1274     "49 866 MS-DOS Russian",
1275     "50 865 MS-DOS Nordic",
1276     "51 864 Arabic",
1277     "52 863 MS-DOS Canadian French",
1278     "53 862 Hebrew",
1279     "54 861 MS-DOS Icelandic",
1280     "55 860 MS-DOS Portuguese",
1281     "56 857 IBM Turkish",
1282     "57 855 IBM Cyrillic; primarily Russian",
1283     "58 852 Latin 2",
1284     "59 775 MS-DOS Baltic",
1285     "60 737 Greek; former 437 G",
1286     "61 708 Arabic; ASMO 708",
1287     "62 850 WE/Latin 1",
1288     "63 437 US",
1289 ]
1290
1291 openTypeOS2CodePageRangesItem = inputItemDict(
1292     title="ulCodePageRange",
1293     hasDefault=False,
1294     controlClass=CheckList,
1295     controlOptions=dict(items=openTypeOS2CodePageRangesOptions),
1296 )
1297
1298 openTypeOS2TypoAscenderItem = inputItemDict(
1299     title="sTypoAscender",
1300     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1301 )
1302 openTypeOS2TypoDescenderItem = inputItemDict(
1303     title="sTypoDescender",
1304     controlClass=NegativeIntegerEditText,
1305     controlOptions=dict(style="number"),
1306     conversionToUFO=noneToZero
1307 )
1308 openTypeOS2TypoLineGapItem = inputItemDict(
1309     title="sTypoLineGap",
1310     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1311 )
1312 openTypeOS2WinAscentItem = inputItemDict(
1313     title="usWinAscent",
1314     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1315 )
1316 openTypeOS2WinDescentItem = inputItemDict(
1317     title="usWinDescent",
1318     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1319 )
1320 openTypeOS2TypeItem = inputItemDict(
1321     title="fsType",
1322     controlClass=EmbeddingControl,
1323     hasDefault=False
1324 )
1325 openTypeOS2SubscriptXSizeItem = inputItemDict(
1326     title="ySubscriptXSize",
1327     controlOptions=dict(style="number", formatter=integerFormatter)
1328 )
1329 openTypeOS2SubscriptYSizeItem = inputItemDict(
1330     title="ySubscriptYSize",
1331     controlOptions=dict(style="number", formatter=integerFormatter)
1332 )
1333 openTypeOS2SubscriptXOffsetItem = inputItemDict(
1334     title="ySubscriptXOffset",
1335     controlOptions=dict(style="number", formatter=integerFormatter)
1336 )
1337 openTypeOS2SubscriptYOffsetItem = inputItemDict(
1338     title="ySubscriptYOffset",
1339     controlOptions=dict(style="number", formatter=integerFormatter)
1340 )
1341 openTypeOS2SuperscriptXSizeItem = inputItemDict(
1342     title="ySuperscriptXSize",
1343     controlOptions=dict(style="number", formatter=integerFormatter)
1344 )
1345 openTypeOS2SuperscriptYSizeItem = inputItemDict(
1346     title="ySuperscriptYSize",
1347     controlOptions=dict(style="number", formatter=integerFormatter)
1348 )
1349 openTypeOS2SuperscriptXOffsetItem = inputItemDict(
1350     title="ySuperscriptXOffset",
1351     controlOptions=dict(style="number", formatter=integerFormatter)
1352 )
1353 openTypeOS2SuperscriptYOffsetItem = inputItemDict(
1354     title="ySuperscriptYOffset",
1355     controlOptions=dict(style="number", formatter=integerFormatter)
1356 )
1357 openTypeOS2StrikeoutSizeItem = inputItemDict(
1358     title="yStrikeoutSize",
1359     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1360 )
1361 openTypeOS2StrikeoutPositionItem = inputItemDict(
1362     title="yStrikeoutPosition",
1363     controlOptions=dict(style="number", formatter=integerFormatter)
1364 )
1365
1366 ## Postscript Identification
1367
1368 postscriptFontNameItem = inputItemDict(
1369     title="FontName"
1370 )
1371 postscriptFullNameItem = inputItemDict(
1372     title="FullName"
1373 )
1374 postscriptWeightNameItem = inputItemDict(
1375     title="WeightName"
1376 )
1377 postscriptUniqueIDItem = inputItemDict(
1378     title="Unique ID Number",
1379     controlOptions=dict(style="idNumber", formatter=integerPositiveFormatter)
1380 )
1381
1382 ## Postscript Hinting
1383
1384 postscriptBlueValuesItem = inputItemDict(
1385     title="BlueValues",
1386     hasDefault=False,
1387     controlOptions=dict(formatter=PostscriptBluesFormatter.alloc().init())
1388 )
1389 postscriptOtherBluesItem = inputItemDict(
1390     title="OtherBlues",
1391     hasDefault=False,
1392     controlOptions=dict(formatter=PostscriptBluesFormatter.alloc().init())
1393 )
1394 postscriptFamilyBluesItem = inputItemDict(
1395     title="FamilyBlues",
1396     hasDefault=False,
1397     controlOptions=dict(formatter=PostscriptBluesFormatter.alloc().init())
1398 )
1399 postscriptFamilyOtherBluesItem = inputItemDict(
1400     title="FamilyOtherBlues",
1401     hasDefault=False,
1402     controlOptions=dict(formatter=PostscriptBluesFormatter.alloc().init())
1403 )
1404 postscriptStemSnapHItem = inputItemDict(
1405     title="StemSnapH",
1406     hasDefault=False,
1407     controlOptions=dict(formatter=PostscriptStemSnapFormatter.alloc().init())
1408 )
1409 postscriptStemSnapVItem = inputItemDict(
1410     title="StemSnapV",
1411     hasDefault=False,
1412     controlOptions=dict(formatter=PostscriptStemSnapFormatter.alloc().init())
1413 )
1414 postscriptBlueFuzzItem = inputItemDict(
1415     title="BlueFuzz",
1416     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1417 )
1418 postscriptBlueShiftItem = inputItemDict(
1419     title="BlueShift",
1420     controlOptions=dict(style="number", formatter=integerPositiveFormatter)
1421 )
1422 postscriptBlueScaleItem = inputItemDict(
1423     title="BlueScale",
1424     controlOptions=dict(style="number", formatter=positiveFloatFormatter)
1425 )
1426 postscriptForceBoldItem = inputItemDict(
1427     title="ForceBold",
1428     controlClass=vanilla.CheckBox
1429 )
1430
1431 ## Postscript Dimensions
1432
1433 postscriptSlantAngleItem = inputItemDict(
1434     title="SlantAngle",
1435     controlOptions=dict(style="number", formatter=floatFormatter)
1436 )
1437 postscriptUnderlineThicknessItem = inputItemDict(
1438     title="UnderlineThickness",
1439     controlOptions=dict(style="number", formatter=integerPositiveFormatter),
1440     hasDefault=True
1441 )
1442 postscriptUnderlinePositionItem = inputItemDict(
1443     title="UnderlinePosition",
1444     controlOptions=dict(style="number", formatter=integerFormatter),
1445     hasDefault=True
1446 )
1447 postscriptIsFixedPitchItem = inputItemDict(
1448     title="isFixedPitched",
1449     controlClass=vanilla.CheckBox
1450 )
1451 postscriptDefaultWidthXItem = inputItemDict(
1452     title="DefaultWidthX",
1453     controlOptions=dict(style="number", formatter=integerPositiveFormatter),
1454     hasDefault=True
1455 )
1456 postscriptNominalWidthXItem = inputItemDict(
1457     title="NominalWidthX",
1458     controlOptions=dict(style="number", formatter=integerPositiveFormatter),
1459     hasDefault=True
1460 )
1461
1462 ## Postscript Characters
1463
1464 postscriptDefaultCharacterItem = inputItemDict(
1465     title="Default Character"
1466 )
1467
1468 postscriptWindowsCharacterSetOptions = [
1469     "Western CP 1252 /ANSI",
1470     "Unknown",
1471     "Symbol",
1472     "Macintosh Mac Roman",
1473     "Japanese Shift JIS",
1474     "Korean EUC-KR or Unified Hangul Code",
1475     "Korean Hangeul (Johab)",
1476     "Simplified Chinese GB2312 (EUC-CN / GBK)",
1477     "Chinese BIG5",
1478     "Greek CP 1253",
1479     "Turkish (Latin 5) CP 1254",
1480     "Vietnamese CP 1258",
1481     "Hebrew CP 1255",
1482     "Arabic CP 1256",
1483     "Baltic CP 1257",
1484     "Bitstream font Set",
1485     "Cyrillic CP 1251",
1486     "Thai",
1487     "Central European CP 1250",
1488     "OEM / DOS"
1489 ]
1490
1491 postscriptWindowsCharacterSetItem = inputItemDict(
1492     title="Microsoft Character Set",
1493     controlClass=vanilla.PopUpButton,
1494     controlOptions=dict(items=postscriptWindowsCharacterSetOptions)
1495 )
1496
1497 ## Miscellaneous
1498
1499 macintoshFONDNameItem = inputItemDict(
1500     title="Font Name"
1501 )
1502 macintoshFONDFamilyIDItem = inputItemDict(
1503     title="Family ID Number",
1504     controlOptions=dict(style="idNumber", formatter=integerPositiveFormatter)
1505 )
1506
1507 # -----------------------------------------------------------------------
1508 # Interface Groups
1509 # These define the grouping and subgrouping of controls in the interface.
1510 # -----------------------------------------------------------------------
1511
1512 allControlDescriptions = dict(
1513     familyName=familyNameItem,
1514     styleName=styleNameItem,
1515     styleMapFamilyName=styleMapFamilyNameItem,
1516     styleMapStyleName=styleMapStyleNameItem,
1517     versionMajor=versionMajorItem,
1518     versionMinor=versionMinorItem,
1519
1520     unitsPerEm=unitsPerEmItem,
1521     descender=descenderItem,
1522     xHeight=xHeightItem,
1523     capHeight=capHeightItem,
1524     ascender=ascenderItem,
1525     italicAngle=italicAngleItem,
1526
1527     copyright=copyrightItem,
1528     trademark=trademarkItem,
1529     openTypeNameLicense=openTypeNameLicenseItem,
1530     openTypeNameLicenseURL=openTypeNameLicenseURLItem,
1531
1532     openTypeNameDesigner=openTypeNameDesignerItem,
1533     openTypeNameDesignerURL=openTypeNameDesignerURLItem,
1534     openTypeNameManufacturer=openTypeNameManufacturerItem,
1535     openTypeNameManufacturerURL=openTypeNameManufacturerURLItem,
1536
1537     note=noteItem,
1538
1539     openTypeHeadCreated=openTypeHeadCreatedItem,
1540     openTypeHeadLowestRecPPEM=openTypeHeadLowestRecPPEMItem,
1541     openTypeHeadFlags=openTypeHeadFlagsItem,
1542
1543     openTypeNamePreferredFamilyName=openTypeNamePreferredFamilyNameItem,
1544     openTypeNamePreferredSubfamilyName=openTypeNamePreferredSubfamilyNameItem,
1545     openTypeNameCompatibleFullName=openTypeNameCompatibleFullNameItem,
1546     openTypeNameWWSFamilyName=openTypeNameWWSFamilyNameItem,
1547     openTypeNameWWSSubfamilyName=openTypeNameWWSSubfamilyNameItem,
1548     openTypeNameVersion=openTypeNameVersionItem,
1549     openTypeNameUniqueID=openTypeNameUniqueIDItem,
1550     openTypeNameDescription=openTypeNameDescriptionItem,
1551     openTypeNameSampleText=openTypeNameSampleTextItem,
1552
1553     openTypeHheaAscender=openTypeHheaAscenderItem,
1554     openTypeHheaDescender=openTypeHheaDescenderItem,
1555     openTypeHheaLineGap=openTypeHheaLineGapItem,
1556     openTypeHheaCaretSlopeRise=openTypeHheaCaretSlopeRiseItem,
1557     openTypeHheaCaretSlopeRun=openTypeHheaCaretSlopeRunItem,
1558     openTypeHheaCaretOffset=openTypeHheaCaretOffsetItem,
1559
1560     openTypeVheaVertTypoAscender=openTypeVheaVertTypoAscenderItem,
1561     openTypeVheaVertTypoDescender=openTypeVheaVertTypoDescenderItem,
1562     openTypeVheaVertTypoLineGap=openTypeVheaVertTypoLineGapItem,
1563     openTypeVheaCaretSlopeRise=openTypeVheaCaretSlopeRiseItem,
1564     openTypeVheaCaretSlopeRun=openTypeVheaCaretSlopeRunItem,
1565     openTypeVheaCaretOffset=openTypeVheaCaretOffsetItem,
1566
1567     openTypeOS2WidthClass=openTypeOS2WidthClassItem,
1568     openTypeOS2WeightClass=openTypeOS2WeightClassItem,
1569     openTypeOS2Selection=openTypeOS2SelectionItem,
1570     openTypeOS2VendorID=openTypeOS2VendorIDItem,
1571     openTypeOS2Panose=openTypeOS2PanoseItem,
1572     openTypeOS2UnicodeRanges=openTypeOS2UnicodeRangesItem,
1573     openTypeOS2CodePageRanges=openTypeOS2CodePageRangesItem,
1574     openTypeOS2TypoAscender=openTypeOS2TypoAscenderItem,
1575     openTypeOS2TypoDescender=openTypeOS2TypoDescenderItem,
1576     openTypeOS2TypoLineGap=openTypeOS2TypoLineGapItem,
1577     openTypeOS2WinAscent=openTypeOS2WinAscentItem,
1578     openTypeOS2WinDescent=openTypeOS2WinDescentItem,
1579     openTypeOS2Type=openTypeOS2TypeItem,
1580     openTypeOS2SubscriptXSize=openTypeOS2SubscriptXSizeItem,
1581     openTypeOS2SubscriptYSize=openTypeOS2SubscriptYSizeItem,
1582     openTypeOS2SubscriptXOffset=openTypeOS2SubscriptXOffsetItem,
1583     openTypeOS2SubscriptYOffset=openTypeOS2SubscriptYOffsetItem,
1584     openTypeOS2SuperscriptXSize=openTypeOS2SuperscriptXSizeItem,
1585     openTypeOS2SuperscriptYSize=openTypeOS2SuperscriptYSizeItem,
1586     openTypeOS2SuperscriptXOffset=openTypeOS2SuperscriptXOffsetItem,
1587     openTypeOS2SuperscriptYOffset=openTypeOS2SuperscriptYOffsetItem,
1588     openTypeOS2StrikeoutSize=openTypeOS2StrikeoutSizeItem,
1589     openTypeOS2StrikeoutPosition=openTypeOS2StrikeoutPositionItem,
1590
1591     postscriptFontName=postscriptFontNameItem,
1592     postscriptFullName=postscriptFullNameItem,
1593     postscriptWeightName=postscriptWeightNameItem,
1594     postscriptUniqueID=postscriptUniqueIDItem,
1595
1596     postscriptBlueValues=postscriptBlueValuesItem,
1597     postscriptOtherBlues=postscriptOtherBluesItem,
1598     postscriptFamilyBlues=postscriptFamilyBluesItem,
1599     postscriptFamilyOtherBlues=postscriptFamilyOtherBluesItem,
1600     postscriptStemSnapH=postscriptStemSnapHItem,
1601     postscriptStemSnapV=postscriptStemSnapVItem,
1602     postscriptBlueFuzz=postscriptBlueFuzzItem,
1603     postscriptBlueShift=postscriptBlueShiftItem,
1604     postscriptBlueScale=postscriptBlueScaleItem,
1605     postscriptForceBold=postscriptForceBoldItem,
1606
1607     postscriptSlantAngle=postscriptSlantAngleItem,
1608     postscriptUnderlineThickness=postscriptUnderlineThicknessItem,
1609     postscriptUnderlinePosition=postscriptUnderlinePositionItem,
1610     postscriptIsFixedPitch=postscriptIsFixedPitchItem,
1611     postscriptDefaultWidthX=postscriptDefaultWidthXItem,
1612     postscriptNominalWidthX=postscriptNominalWidthXItem,
1613
1614     postscriptDefaultCharacter=postscriptDefaultCharacterItem,
1615     postscriptWindowsCharacterSet=postscriptWindowsCharacterSetItem,
1616
1617     macintoshFONDName=macintoshFONDNameItem,
1618     macintoshFONDFamilyID=macintoshFONDFamilyIDItem,
1619 )
1620
1621 controlOrganization=[
1622     dict(
1623         title="General",
1624         customView=None,
1625         groups = [
1626             ("Identification",
1627                 "familyName",
1628                 "styleName",
1629                 "styleMapFamilyName",
1630                 "styleMapStyleName",
1631                 "versionMajor",
1632                 "versionMinor"
1633             ),
1634             ("Dimensions",
1635                 "unitsPerEm",
1636                 "descender",
1637                 "xHeight",
1638                 "capHeight",
1639                 "ascender",
1640                 "italicAngle"
1641             ),
1642             ("Legal",
1643                 "copyright",
1644                 "trademark",
1645                 "openTypeNameLicense",
1646                 "openTypeNameLicenseURL"
1647             ),
1648             ("Parties",
1649                 "openTypeNameDesigner",
1650                 "openTypeNameDesignerURL",
1651                 "openTypeNameManufacturer",
1652                 "openTypeNameManufacturerURL"
1653             ),
1654             ("Note", "note")
1655         ]
1656     ),
1657     dict(
1658         title="OpenType",
1659         customView=None,
1660         groups = [
1661             ("head Table",
1662                 "openTypeHeadCreated",
1663                 "openTypeHeadLowestRecPPEM",
1664                 "openTypeHeadFlags"
1665             ),
1666             ("name Table",
1667                 "openTypeNamePreferredFamilyName",
1668                 "openTypeNamePreferredSubfamilyName",
1669                 "openTypeNameCompatibleFullName",
1670                 "openTypeNameWWSFamilyName",
1671                 "openTypeNameWWSSubfamilyName",
1672                 "openTypeNameVersion",
1673                 "openTypeNameUniqueID",
1674                 "openTypeNameDescription",
1675                 "openTypeNameSampleText"
1676             ),
1677             ("hhea Table",
1678                 "openTypeHheaAscender",
1679                 "openTypeHheaDescender",
1680                 "openTypeHheaLineGap",
1681                 "openTypeHheaCaretSlopeRise",
1682                 "openTypeHheaCaretSlopeRun",
1683                 "openTypeHheaCaretOffset"
1684             ),
1685             ("vhea Table",
1686                 "openTypeVheaVertTypoAscender",
1687                 "openTypeVheaVertTypoDescender",
1688                 "openTypeVheaVertTypoLineGap",
1689                 "openTypeVheaCaretSlopeRise",
1690                 "openTypeVheaCaretSlopeRun",
1691                 "openTypeVheaCaretOffset"
1692             ),
1693             ("OS/2 Table",
1694                 "openTypeOS2WidthClass",
1695                 "openTypeOS2WeightClass",
1696                 "openTypeOS2Selection",
1697                 "openTypeOS2VendorID",
1698                 "openTypeOS2Type",
1699                 "openTypeOS2UnicodeRanges",
1700                 "openTypeOS2CodePageRanges",
1701                 "openTypeOS2TypoAscender",
1702                 "openTypeOS2TypoDescender",
1703                 "openTypeOS2TypoLineGap",
1704                 "openTypeOS2WinAscent",
1705                 "openTypeOS2WinDescent",
1706                 "openTypeOS2SubscriptXSize",
1707                 "openTypeOS2SubscriptYSize",
1708                 "openTypeOS2SubscriptXOffset",
1709                 "openTypeOS2SubscriptYOffset",
1710                 "openTypeOS2SuperscriptXSize",
1711                 "openTypeOS2SuperscriptYSize",
1712                 "openTypeOS2SuperscriptXOffset",
1713                 "openTypeOS2SuperscriptYOffset",
1714                 "openTypeOS2StrikeoutSize",
1715                 "openTypeOS2StrikeoutPosition",
1716                 "openTypeOS2Panose"
1717             )
1718         ]
1719     ),
1720     dict(
1721         title="Postscript",
1722         customView=None,
1723         groups = [
1724             ("Identification",
1725                 "postscriptFontName",
1726                 "postscriptFullName",
1727                 "postscriptWeightName",
1728                 "postscriptUniqueID"
1729             ),
1730             ("Hinting",
1731                 "postscriptBlueValues",
1732                 "postscriptOtherBlues",
1733                 "postscriptFamilyBlues",
1734                 "postscriptFamilyOtherBlues",
1735                 "postscriptStemSnapH",
1736                 "postscriptStemSnapV",
1737                 "postscriptBlueFuzz",
1738                 "postscriptBlueShift",
1739                 "postscriptBlueScale",
1740                 "postscriptForceBold"
1741             ),
1742             ("Dimensions",
1743                 "postscriptSlantAngle",
1744                 "postscriptUnderlineThickness",
1745                 "postscriptUnderlinePosition",
1746                 "postscriptIsFixedPitch",
1747                 "postscriptDefaultWidthX",
1748                 "postscriptNominalWidthX"
1749             ),
1750             ("Characters",
1751                 "postscriptDefaultCharacter",
1752                 "postscriptWindowsCharacterSet"
1753             )
1754         ]
1755     ),
1756     dict(
1757         title="Miscellaneous",
1758         customView=None,
1759         groups = [
1760             ("FOND Data",
1761                 "macintoshFONDName",
1762                 "macintoshFONDFamilyID"
1763             )
1764         ]
1765     ),
1766 ]
1767
1768
1769 ## Toolbar
1770
1771 toolbarColor1 = NSColor.colorWithCalibratedWhite_alpha_(.4, .6)
1772 toolbarColor2 = NSColor.colorWithCalibratedWhite_alpha_(.4, .2)
1773 toolbarColor3 = NSColor.colorWithCalibratedWhite_alpha_(.65, 1)
1774 toolbarColorFallback = NSColor.colorWithCalibratedWhite_alpha_(0, .25)
1775
1776
1777 class DefconAppKitFontInfoToolbarView(NSView):
1778
1779     def drawRect_(self, rect):
1780         bounds = self.bounds()
1781         bounds = NSInsetRect(bounds, .5, .5)
1782         # fill
1783         fillPath = roundedRectBezierPath(bounds, 5, roundLowerLeft=False, roundLowerRight=False)
1784         # 10.5+
1785         try:
1786             gradient = NSGradient.alloc().initWithColors_([toolbarColor1, toolbarColor2])
1787             gradient.drawInBezierPath_angle_(fillPath, 90)
1788         except NameError:
1789             toolbarColorFallback.set()
1790             fillPath.fill()
1791         # stroke
1792         strokePath = roundedRectBezierPath(bounds, 5, roundLowerLeft=False, roundLowerRight=False, closeBottom=False)
1793         strokePath.setLineWidth_(1)
1794         toolbarColor3.set()
1795         strokePath.stroke()
1796
1797
1798 class FontInfoToolbar(vanilla.Group):
1799
1800     nsViewClass = DefconAppKitFontInfoToolbarView
1801     nsButtonType = NSOnOffButton
1802
1803
1804 class FontInfoToolbarButton(vanilla.Button):
1805
1806     nsBezelStyle = NSRoundRectBezelStyle
1807     frameAdjustments = {
1808         "mini": (0, 0, 0, 0),
1809         "small": (0, 0, 0, 0),
1810         "regular": (0, 0, 0, 0),
1811         }
1812
1813 ## Group View
1814
1815 class DefconAppKitFontInfoSectionView(NSView):
1816
1817     def viewDidMoveToWindow(self):
1818         if hasattr(self, "vanillaWrapper") and self.vanillaWrapper() is not None:
1819             v = self.vanillaWrapper()
1820             v._scrollView.setPosSize(v._scrollView._posSize)
1821
1822
1823 class DefconAppKitFontInfoCategoryControlsGroup(NSView):
1824
1825     def isFlipped(self):
1826         return True
1827
1828     def viewDidMoveToWindow(self):
1829         if hasattr(self, "_haveMovedToWindow"):
1830             return
1831         self._haveMovedToWindow = True
1832         scrollView = self.enclosingScrollView()
1833         clipView = scrollView.contentView()
1834         pt = (0, 0)
1835         clipView.scrollToPoint_(pt)
1836         scrollView.reflectScrolledClipView_(clipView)
1837
1838
1839 class FontInfoCategoryControlsGroup(vanilla.Group):
1840
1841     nsViewClass = DefconAppKitFontInfoCategoryControlsGroup
1842
1843
1844 backgroundColor = NSColor.colorWithCalibratedWhite_alpha_(.93, 1)
1845
1846
1847 class FontInfoSection(vanilla.Group):
1848
1849     nsViewClass = DefconAppKitFontInfoSectionView
1850
1851     def __init__(self, posSize, groupOrganization, controlDescriptions, font):
1852         super(FontInfoSection, self).__init__(posSize)
1853         self._finishedSetup = False
1854         self._font = font
1855         left, top, width, height = posSize
1856         ## reference storage
1857         self._jumpButtons = {}
1858         self._groupTitlePositions = {}
1859         self._controlToAttributeData = {}
1860         self._attributeToControl = {}
1861         self._defaultControlToAttribute = {}
1862         self._attributeToDefaultControl = {}
1863         ## top navigation
1864         self._buttonBar = FontInfoToolbar((0, 12, -0, 60))
1865         groupTitles = [group[0] for group in groupOrganization]
1866         if len(groupTitles) > 1:
1867             buttonFont = FontInfoToolbarButton((0, 0, 0, 0), "", sizeStyle="small").getNSButton().font()
1868             attributes = {NSFontNameAttribute : buttonFont}
1869             buttonWidth = 18 + max([NSString.stringWithString_(title).sizeWithAttributes_(attributes)[0] for title in groupTitles])
1870             buttonBufferWidth = 5
1871             buttonGroupWidth = buttonWidth * len(groupTitles)
1872             buttonGroupWidth += buttonBufferWidth * (len(groupTitles) - 1)
1873             left = (width - buttonGroupWidth) / 2
1874             for index, groupTitle in enumerate(groupTitles):
1875                 attribute = "jumpButton%d" % index
1876                 jumpButton = FontInfoToolbarButton((left, 25, buttonWidth, 17), groupTitle, sizeStyle="small", callback=self._jumpButtonCallback)
1877                 setattr(self._buttonBar, attribute, jumpButton)
1878                 left += buttonWidth
1879                 left += buttonBufferWidth
1880                 self._jumpButtons[jumpButton] = index
1881         ## controls
1882         controlView = FontInfoCategoryControlsGroup((0, 0, 10, 10))
1883         # positions and sizes
1884         controlViewHeight = 0
1885         controlViewWidth = width - 16
1886         groupTitleLeft = 10
1887         groupTitleWidth = controlViewWidth - 20
1888         itemTitleLeft = 10
1889         itemTitleWidth = 175
1890         itemInputLeft = itemTitleLeft + itemTitleWidth + 5
1891         itemInputStringWidth = controlViewWidth - 10 - itemInputLeft
1892         itemWidths = {
1893             "idNumber" : 140,
1894             "number" : 70,
1895             vanilla.EditText : itemInputStringWidth,
1896             vanilla.RadioGroup : itemInputStringWidth,
1897             vanilla.PopUpButton : itemInputStringWidth,
1898             CheckList : itemInputStringWidth,
1899             vanilla.DatePicker : itemInputStringWidth,
1900             vanilla.CheckBox : 22,
1901             PanoseControl : controlViewWidth,
1902             EmbeddingControl : itemInputStringWidth,
1903         }
1904         # run through the groups
1905         currentTop = -10
1906         for groupIndex, group in enumerate(groupOrganization):
1907             # group title
1908             self._groupTitlePositions[groupIndex] = currentTop
1909             currentTop -= 17
1910             groupTitleAttribute = "groupTitle%d" % groupIndex
1911             groupTitle = group[0]
1912             groupTitleControl = vanilla.TextBox((groupTitleLeft, currentTop, groupTitleWidth, 17), groupTitle)
1913             setattr(controlView, groupTitleAttribute, groupTitleControl)
1914             # group title line
1915             currentTop -= 5
1916             groupTitleLineAttribute = "groupTitleLine%d" % groupIndex
1917             groupTitleLineControl = vanilla.HorizontalLine((groupTitleLeft, currentTop, groupTitleWidth, 1))
1918             setattr(controlView, groupTitleLineAttribute, groupTitleLineControl)
1919             currentTop -= 15
1920             # run through the controls
1921             for fontAttribute in group[1:]:
1922                 item = controlDescriptions[fontAttribute]
1923                 # title
1924                 itemTitle = item["title"]
1925                 if itemTitle:
1926                     itemTitle += ":"
1927                 # item title
1928                 if itemTitle is not None:
1929                     itemTitleAttribute = "itemTitle_%s" % fontAttribute
1930                     itemTitleControl = vanilla.TextBox((itemTitleLeft, currentTop-19, itemTitleWidth, 17), itemTitle, alignment="right")
1931                     setattr(controlView, itemTitleAttribute, itemTitleControl)
1932                 # control
1933                 itemClass = item["controlClass"]
1934                 itemOptions = item.get("controlOptions", {})
1935                 itemWidthKey = itemOptions.get("style", itemClass)
1936                 itemWidth = itemWidths[itemWidthKey]
1937                 if itemClass == vanilla.EditText:
1938                     if itemOptions.get("lineCount", 1) != 1:
1939                         itemClass = vanilla.TextEditor
1940                 ## EditText
1941                 if itemClass == vanilla.EditText or itemClass==NegativeIntegerEditText:
1942                     itemHeight = 22
1943                     currentTop -= itemHeight
1944                     itemAttribute = "inputEditText_%s" % fontAttribute
1945                     itemControl = itemClass((itemInputLeft, currentTop, itemWidth, itemHeight), callback=self._controlEditCallback, formatter=itemOptions.get("formatter"))
1946                     setattr(controlView, itemAttribute, itemControl)
1947                 ## TextEditor
1948                 elif itemClass == vanilla.TextEditor:
1949                     itemHeight = (itemOptions["lineCount"] * 14) + 8
1950                     currentTop -= itemHeight
1951                     itemAttribute = "inputTextEditor_%s" % fontAttribute
1952                     if not itemTitle:
1953                         l = groupTitleLeft
1954                         w = groupTitleWidth
1955                     else:
1956                         l = itemInputLeft
1957                         w = itemWidth
1958                     itemControl = itemClass((l, currentTop, w, itemHeight), callback=self._controlEditCallback)
1959                     setattr(controlView, itemAttribute, itemControl)
1960                 ## RadioGroup
1961                 elif itemClass == vanilla.RadioGroup:
1962                     radioOptions = itemOptions["items"]
1963                     itemHeight = 20 * len(radioOptions)
1964                     currentTop -= itemHeight
1965                     itemAttribute = "inputRadioGroup_%s" % fontAttribute
1966                     itemControl = itemClass((itemInputLeft, currentTop-2, itemWidth, itemHeight), radioOptions, callback=self._controlEditCallback)
1967                     setattr(controlView, itemAttribute, itemControl)
1968                 ## CheckBox
1969                 elif itemClass == vanilla.CheckBox:
1970                     itemHeight = 22
1971                     currentTop -= itemHeight
1972                     itemAttribute = "inputCheckBox_%s" % fontAttribute
1973                     itemControl = itemClass((itemInputLeft, currentTop-1, itemWidth, itemHeight), "", callback=self._controlEditCallback)
1974                     setattr(controlView, itemAttribute, itemControl)
1975                 ## PopUpButton
1976                 elif itemClass == vanilla.PopUpButton:
1977                     itemHeight = 20
1978                     currentTop -= itemHeight
1979                     popupOptions = itemOptions["items"]
1980                     itemAttribute = "inputPopUpButton_%s" % fontAttribute
1981                     itemControl = itemClass((itemInputLeft, currentTop-2, itemWidth, itemHeight), popupOptions, callback=self._controlEditCallback)
1982                     setattr(controlView, itemAttribute, itemControl)
1983                 ## CheckList
1984                 elif itemClass == CheckList:
1985                     listOptions = itemOptions["items"]
1986                     itemHeight = 200
1987                     if len(listOptions) * 20 < itemHeight:
1988                         itemHeight = len(listOptions) * 20
1989                     currentTop -= itemHeight
1990                     itemAttribute = "inputCheckList_%s" % fontAttribute
1991                     itemControl = itemClass((itemInputLeft, currentTop, itemWidth, itemHeight), listOptions, callback=self._controlEditCallback)
1992                     setattr(controlView, itemAttribute, itemControl)
1993                 ## DatePicker
1994                 elif itemClass == vanilla.DatePicker:
1995                     now = NSDate.date()
1996                     minDate = NSDate.dateWithString_("1904-01-01 00:00:01 +0000")
1997                     minDate = None
1998                     itemHeight = 27
1999                     currentTop -= itemHeight
2000                     itemAttribute = "inputDatePicker_%s" % fontAttribute
2001                     itemControl = itemClass((itemInputLeft, currentTop+5, itemWidth, itemHeight), date=now, minDate=minDate, callback=self._controlEditCallback)
2002                     setattr(controlView, itemAttribute, itemControl)
2003                 ## Panose
2004                 elif itemClass == PanoseControl:
2005                     itemHeight = 335
2006                     currentTop -= itemHeight
2007                     itemAttribute = "inputPanoseControl_%s" % fontAttribute
2008                     itemControl = itemClass((10, currentTop, itemWidth, itemHeight), 0, itemTitleWidth, itemInputLeft-10, itemInputStringWidth, self._controlEditCallback)
2009                     setattr(controlView, itemAttribute, itemControl)
2010                 ## Embedding
2011                 elif itemClass == EmbeddingControl:
2012                     itemHeight = 75
2013                     currentTop -= itemHeight
2014                     itemAttribute = "inputEmbeddingControl_%s" % fontAttribute
2015                     itemControl = itemClass((itemInputLeft, currentTop, itemWidth, itemHeight), self._controlEditCallback)
2016                     setattr(controlView, itemAttribute, itemControl)
2017                 else:
2018                     print itemClass
2019                     continue
2020                 ## default
2021                 if item["hasDefault"]:
2022                     currentTop -= 17
2023                     defaultControl = vanilla.CheckBox((itemInputLeft, currentTop, 100, 10), "Use Default Value", sizeStyle="mini", callback=self._useDefaultCallback)
2024                     defaultAttribute = "inputDefaultCheckBox_%s" % fontAttribute
2025                     setattr(controlView, defaultAttribute, defaultControl)
2026                     self._defaultControlToAttribute[defaultControl] = fontAttribute
2027                     self._attributeToDefaultControl[fontAttribute] = defaultControl
2028                 ## store
2029                 item["fontAttribute"] = fontAttribute
2030                 self._controlToAttributeData[itemControl] = item
2031                 self._attributeToControl[fontAttribute] = itemControl
2032                 ## final offset
2033                 currentTop -= 15
2034
2035
2036         # scroll view
2037         height = abs(currentTop)
2038         self._scrollView = vanilla.ScrollView((0, 62, -0, -0), controlView.getNSView(), backgroundColor=backgroundColor, hasHorizontalScroller=False)
2039         controlView.setPosSize((0, 0, width, height))
2040         controlView._setFrame(((0, 0), (width, height)))
2041         size = controlView.getNSView().frame().size
2042         controlView.getNSView().setFrame_(((0, 0), size))
2043
2044         ##load info
2045         self._loadInfo()
2046         self._finishedSetup = True
2047
2048     def _breakCycles(self):
2049         self._jumpButtons = []
2050         super(FontInfoSection, self)._breakCycles()
2051
2052     def _loadInfo(self):
2053         for attribute, control in self._attributeToControl.items():
2054             value = getattr(self._font.info, attribute)
2055             attributeData = self._controlToAttributeData[control]
2056             # handle the default control
2057             if attributeData["hasDefault"]:
2058                 defaultControl = self._attributeToDefaultControl[attribute]
2059                 defaultControl.set(value is None)
2060                 control.enable(value is not None)
2061             # handle the main control
2062             if value is not None:
2063                 # convert
2064                 conversionFunction = attributeData["conversionFromUFO"]
2065                 if conversionFunction:
2066                     value = conversionFunction(value)
2067                 # set
2068                 control.set(value)
2069
2070     # control view shortcut
2071
2072     def _get_controlView(self):
2073         scrollView = self._scrollView.getNSScrollView()
2074         return scrollView.documentView()
2075
2076     _controlView = property(_get_controlView)
2077
2078     # navigation
2079
2080     def _jumpButtonCallback(self, sender):
2081         scrollView = self._scrollView.getNSScrollView()
2082         clipView = scrollView.contentView()
2083         documentView = scrollView.documentView()
2084         index = self._jumpButtons[sender]
2085         viewH = documentView.bounds().size[1]
2086         clipViewH = clipView.bounds().size[1]
2087         y = clipViewH - self._groupTitlePositions[index]
2088         y -= 10
2089         if y > viewH:
2090             y = NSMaxY(documentView.frame()) - clipViewH
2091         pt = (0, y)
2092         clipView.scrollToPoint_(pt)
2093         scrollView.reflectScrolledClipView_(clipView)
2094
2095     # callbacks
2096
2097     def _controlEditCallback(self, sender):
2098         if not self._finishedSetup:
2099             return
2100         attributeData = self._controlToAttributeData[sender]
2101         attribute = attributeData["fontAttribute"]
2102         conversionFunction = attributeData["conversionToUFO"]
2103         # get the value
2104         value = sender.get()
2105         # convert
2106         if isinstance(value, NSArray):
2107             value = list(value)
2108         elif isinstance(value, long):
2109             value = int(value)
2110         if conversionFunction is not None:
2111             value = conversionFunction(value)
2112         # set
2113         setattr(self._font.info, attribute, value)
2114
2115     def _useDefaultCallback(self, sender):
2116         state = sender.get()
2117         fontAttribute = self._defaultControlToAttribute[sender]
2118         control = self._attributeToControl[fontAttribute]
2119         attributeData = self._controlToAttributeData[control]
2120         # get the value
2121         if state:
2122             value = None
2123         else:
2124             value = getAttrWithFallback(self._font.info, fontAttribute)
2125         # set in the font
2126         setattr(self._font.info, fontAttribute, value)
2127         # convert for the interface
2128         if value is not None:
2129             conversionFunction = attributeData["conversionFromUFO"]
2130             if conversionFunction is not None:
2131                 value = conversionFunction(value)
2132         # update the control
2133         control.enable(not state)
2134         if value is None:
2135             if isinstance(control, vanilla.EditText):
2136                 control.set("")
2137         else:
2138             control.set(value)
2139
2140
2141 # ---------
2142 # main view
2143 # ---------
2144
2145
2146 class FontInfoView(vanilla.Tabs):
2147
2148     def __init__(self, posSize, font):
2149         sectionNames = [section["title"] for section in controlOrganization]
2150         super(FontInfoView, self).__init__(posSize, sectionNames)
2151         self._nsObject.setTabViewType_(NSNoTabsNoBorder)
2152         left, top, width, height = posSize
2153         assert width > 0
2154         # controls
2155         buttonWidth = 340
2156         buttonLeft = (posSize[2] - buttonWidth) / 2
2157         segments = [dict(title=sectionName) for sectionName in sectionNames]
2158         self._segmentedButton = vanilla.SegmentedButton((buttonLeft, -26, buttonWidth, 24), segments, callback=self._tabSelectionCallback, sizeStyle="regular")
2159         self._segmentedButton.set(0)
2160         # sections
2161         for index, sectionData in enumerate(controlOrganization):
2162             viewClass = sectionData["customView"]
2163             if viewClass is not None:
2164                 self[index].section = viewClass((0, 0, width, 0), font)
2165             else:
2166                 self[index].section = FontInfoSection((0, 0, width, 0), sectionData["groups"], allControlDescriptions, font)
2167
2168     def _tabSelectionCallback(self, sender):
2169         self.set(sender.get())
2170
Note: See TracBrowser for help on using the browser.