3
// Copyright (C) 2008 - 2011 Patrick Ulbrich
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
// GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program. If not, see <http://www.gnu.org/licenses/>.
20
using System.Collections.Generic;
24
using Basenji.Gui.Base;
26
using VolumeDB.Metadata;
28
namespace Basenji.Gui.Widgets
30
public partial class ItemInfo : BinBase
32
private const int MAX_PREVIEW_WIDTH = 128; // native thumbnail size
33
/* The previews height is determined by its parent Iteminfo widget automatically,
34
* which should be ~MAX_PREVIEW_WIDTH.
35
* Don't force a height or the iteminfo widget will do crazy
36
* up/down jumps when showing/hiding the preview.
37
private const int MAX_PREVIEW_HEIGHT = 128; // native thumbnail size
40
private static readonly string STR_BY = S._("by");
41
private static readonly string STR_FROM = S._("from");
43
private bool isMinimized;
50
public void ShowInfo(VolumeItem item, VolumeDatabase db) {
52
throw new ArgumentNullException("item");
54
throw new ArgumentNullException("db");
56
// update item preview
57
itemPreview.Preview(item, db);
58
if (!itemPreview.EnableGenericIcons && !Minimized) {
59
if (itemPreview.IsThumbnailPreview)
65
// update item properties
66
FillPropertyBox(item);
69
// somehow the cells of the outer hbox
70
// don't match the gradient of this widget
71
// if ShowInfo() is called a second time.
82
public bool Minimized {
89
if (!itemPreview.EnableGenericIcons) {
90
// do not show hidden generic icon
91
if (itemPreview.IsThumbnailPreview || value)
92
itemPreview.Visible = !value;
94
itemPreview.Visible = !value;
97
if (propertyBox.Minimized != value)
98
propertyBox.Minimized = value;
102
private void FillPropertyBox(VolumeItem item) {
103
ItemProperty[] properties;
104
Dictionary<string, string> nameProperty;
106
switch (item.GetVolumeItemType()) {
107
case VolumeItemType.FileVolumeItem:
108
case VolumeItemType.DirectoryVolumeItem:
110
ItemProperty.GetFSItemProperties((FileSystemVolumeItem)item,
114
case VolumeItemType.AudioTrackVolumeItem:
116
ItemProperty.GetAudioCdItemProperties((AudioTrackVolumeItem)item,
121
throw new NotImplementedException("Iteminfo has not been implemented for this itemtype yet");
124
propertyBox.SetProperties(properties);
127
if (nameProperty.TryGetValue("title", out tmp)) {
128
// title found (e.g. html or doc file)
129
// may be followed optionally by artist and/or album (audio file)
130
StringBuilder sbName = new StringBuilder();
131
StringBuilder sbTooltip = new StringBuilder();
133
sbName.AppendFormat("<b>{0}</b>", Util.Escape(tmp));
134
sbTooltip.Append(tmp);
136
if (nameProperty.TryGetValue("artist", out tmp)) {
137
sbName.AppendFormat(" <i>{0}</i> {1}", STR_BY, Util.Escape(tmp));
138
sbTooltip.AppendFormat(" {0} {1}", STR_BY, tmp);
141
if (nameProperty.TryGetValue("album", out tmp)) {
142
sbName.AppendFormat(" <i>{0}</i> {1}", STR_FROM, Util.Escape(tmp));
143
sbTooltip.AppendFormat(" {0} {1}", STR_FROM, tmp);
146
propertyBox.SetNameProperty(sbName.ToString(), sbTooltip.ToString());
149
if (!nameProperty.TryGetValue("name", out tmp))
150
throw new ArgumentException("Name expected");
152
propertyBox.SetNameProperty(string.Format("<b>{0}</b>", Util.Escape(tmp)), tmp);
156
#region ItemProperty class
157
// helper class that returns properties sorted by priority
158
private class ItemProperty : IComparable<ItemProperty>
164
private static readonly string[] KEYWORD_SEPARATORS = { "; " };
166
private ItemProperty(string name, string value, int priority) {
169
this.priority = priority;
172
public int CompareTo(ItemProperty p) {
173
return (this.priority - p.priority);
176
public static void GetFSItemProperties(FileSystemVolumeItem item,
177
out ItemProperty[] properties,
178
out Dictionary<string, string> nameProperty) {
180
List<ItemProperty> tmp;
181
GetCommonItemProperties(item, out tmp, out nameProperty);
183
tmp.Add(new ItemProperty(S._("Location"), item.Location, 202));
184
tmp.Add(new ItemProperty(S._("Last write time"), item.LastWriteTime.ToString(), 205));
186
if (item.IsSymLink) {
187
FileSystemVolumeItem targetItem = item.GetSymLinkTargetItem();
188
string symlinkTargetPath = null;
190
if (targetItem.Location != "/" && targetItem.Name != "/")
191
symlinkTargetPath = string.Format("{0}/{1}", targetItem.Location, targetItem.Name);
193
symlinkTargetPath = targetItem.Location + targetItem.Name;
195
tmp.Add(new ItemProperty(S._("Symlink target"), symlinkTargetPath, 203));
198
if (item is FileVolumeItem) {
199
FileVolumeItem fvi = (FileVolumeItem)item;
200
string sizeStr = Util.GetSizeStr(fvi.Size);
201
string hash = fvi.Hash;
203
tmp.Add(new ItemProperty(S._("Size"), sizeStr, 204));
204
if (!string.IsNullOrEmpty(hash))
205
tmp.Add(new ItemProperty(S._("Hash"), hash, 207));
208
if (!string.IsNullOrEmpty(item.MimeType))
209
tmp.Add(new ItemProperty(S._("Filetype"), item.MimeType, 206));
211
tmp.Sort(); // sort by priority
212
properties = tmp.ToArray();
215
public static void GetAudioCdItemProperties(AudioTrackVolumeItem item,
216
out ItemProperty[] properties,
217
out Dictionary<string, string> nameProperty) {
219
const int PCM_FACTOR = (44100 * 16 * 2) / 8;
221
List<ItemProperty> tmp;
222
GetCommonItemProperties(item, out tmp, out nameProperty);
224
tmp.Add(new ItemProperty(S._("Duration"), FormatDuration(item.Duration), 202));
225
tmp.Add(new ItemProperty(S._("Size"), Util.GetSizeStr((long)(item.Duration.TotalSeconds * PCM_FACTOR)), 203));
226
tmp.Add(new ItemProperty(S._("Track No."), (item.ItemID - 1).ToString(), 204));
228
if (!string.IsNullOrEmpty(item.MimeType))
229
tmp.Add(new ItemProperty(S._("Type"), item.MimeType, 205));
231
tmp.Sort(); // sort by priority
232
properties = tmp.ToArray();
235
private static void GetCommonItemProperties(VolumeItem item,
236
out List<ItemProperty> properties,
237
out Dictionary<string, string> nameProperty) {
239
List<ItemProperty> tmp = new List<ItemProperty>();
240
nameProperty = new Dictionary<string, string>();
243
// add metadata properties first (higher priority)
246
if (!item.MetaData.IsEmpty) {
247
Dictionary<MetadataType, string> metadata = item.MetaData.ToDictionary();
250
// cherry-pick interesting properties
254
/* audio properties*/
255
if (metadata.TryGetValue(MetadataType.GENRE, out val)) {
256
// NOTE: genre keyword is used in e.g. deb packages as well
257
tmp.Add(new ItemProperty(S._("Genre"), RemoveSimilarIDTags(val), 105));
260
if (metadata.TryGetValue(MetadataType.ARTIST, out val)) {
261
//tmp2.Add(new ItemProperty(S._("Artist"), val, 101));
262
nameProperty.Add("artist", RemoveSimilarIDTags(val));
265
if (metadata.TryGetValue(MetadataType.TITLE, out val)) {
266
// NOTE: title keyword is used in e.g. html or doc files as well
267
//tmp2.Add(new ItemProperty(S._("Title"), val, 101));
268
nameProperty.Add("title", RemoveSimilarIDTags(val));
271
if (metadata.TryGetValue(MetadataType.ALBUM, out val)) {
272
//tmp2.Add(new ItemProperty(S._("Album"), val, 103));
273
nameProperty.Add("album", RemoveSimilarIDTags(val));
276
if (metadata.TryGetValue(MetadataType.YEAR, out val)) {
277
tmp.Add(new ItemProperty(S._("Year"), RemoveSimilarIDTags(val), 104));
280
if (metadata.TryGetValue(MetadataType.DESCRIPTION, out val)) {
281
// NOTE: description keyword is used in e.g. html files as well
282
tmp.Add(new ItemProperty(S._("Description"), RemoveSimilarIDTags(val), 110));
285
/* audio / picture / video properties */
286
if (metadata.TryGetValue(MetadataType.DURATION, out val)) {
287
tmp.Add(new ItemProperty(S._("Duration"), FormatDuration(MetadataUtils.MetadataDurationToTimespan(val)), 106));
290
if (metadata.TryGetValue(MetadataType.SIZE, out val)) {
291
// NOTE: size keyword is used in e.g. deb packages as well (unpacked size in kb)
292
if (item.MimeType.StartsWith("image") || item.MimeType.StartsWith("video"))
293
tmp.Add(new ItemProperty(S._("Dimensions"), val, 107));
296
/* other properties*/
297
if (metadata.TryGetValue(MetadataType.FORMAT, out val)) {
298
tmp.Add(new ItemProperty(S._("Format"), val, 108));
301
if (metadata.TryGetValue(MetadataType.AUTHOR, out val)) {
302
tmp.Add(new ItemProperty(S._("Author"), val, 111));
305
if (metadata.TryGetValue(MetadataType.COPYRIGHT, out val)) {
306
tmp.Add(new ItemProperty(S._("Copyright"), val, 112));
309
if (metadata.TryGetValue(MetadataType.PRODUCER, out val)) {
310
tmp.Add(new ItemProperty(S._("Producer"), val, 115));
313
if (metadata.TryGetValue(MetadataType.CREATOR, out val)) {
314
tmp.Add(new ItemProperty(S._("Creator"), val, 114));
317
if (metadata.TryGetValue(MetadataType.SOFTWARE, out val)) {
318
tmp.Add(new ItemProperty(S._("Software"), val, 116));
321
if (metadata.TryGetValue(MetadataType.LANGUAGE, out val)) {
322
tmp.Add(new ItemProperty(S._("Language"), val, 113));
325
if (metadata.TryGetValue(MetadataType.PAGE_COUNT, out val)) {
326
tmp.Add(new ItemProperty(S._("Page count"), val, 109));
329
if (metadata.TryGetValue(MetadataType.FILENAME, out val)) {
330
// count files in archives
331
// (filenames were joined by MetadataStore.ToDictionary())
332
string[] filenames = val.Split(KEYWORD_SEPARATORS, StringSplitOptions.None);
333
tmp.Add(new ItemProperty(S._("File count"), filenames.Length.ToString(), 117));
336
if (Global.EnableDebugging) {
337
foreach (KeyValuePair<MetadataType, string> pair in metadata) {
338
Platform.Common.Diagnostics.Debug.WriteLine(
339
String.Format("{0}: {1}", pair.Key, pair.Value));
346
// add common item properties (low priority, shown only if there's room left)
349
if (!nameProperty.ContainsKey("title")) {
351
// or artist and/or album metadata only
352
nameProperty.Clear();
354
nameProperty.Add("name", item.Name);
357
if (item.Note.Length > 0)
358
tmp.Add(new ItemProperty(S._("Note"), item.Note, 301));
360
if (item.Keywords.Length > 0)
361
tmp.Add(new ItemProperty(S._("Keywords"), item.Keywords, 302));
366
private static string RemoveSimilarIDTags(string separatedTags) {
367
// Dirty hack, tries to remove similar IDv2/IDv3 tag keywords,
368
// i.e. tags that were equal in the IDv2 and the IDv3 header up to a certain length.
369
// (e.g "MP3 Title Number 1" (IDv3 tag) and "MP3 Title Numb" (IDv2 tag)
370
// In this case the shorter one is removed.
371
// Fully equal keywords can not occur, they have been filtered out in the scanning process.
372
// NOTE: This function may fail if the keyword itself contains the separator string ("; ").
374
if (separatedTags.IndexOf(KEYWORD_SEPARATORS[0]) > -1) {
375
string[] tags = separatedTags.Split(KEYWORD_SEPARATORS, StringSplitOptions.None);
376
if (tags.Length == 2) {
377
if (tags[0].StartsWith(tags[1]))
379
else if (tags[1].StartsWith(tags[0]))
383
return separatedTags;
386
private static string FormatDuration(TimeSpan duration) {
387
// duration.ToString() also returns ms.
388
return string.Format("{0:D2}:{1:D2}:{2:D2}",
397
// gui initialization
398
public partial class ItemInfo : BinBase
400
private HBox outerBox;
401
private ItemPreview itemPreview;
402
private PropertyBox propertyBox;
404
protected override void BuildGui() {
405
itemPreview = new ItemPreview();
406
itemPreview.RoundedCorners = true;
407
itemPreview.EnableGenericIcons = false;
408
/*itemPreview.HeightRequest = MAX_PREVIEW_HEIGHT;*/
409
itemPreview.WidthRequest = MAX_PREVIEW_WIDTH;
410
// set no_window flag to disable background drawing
411
itemPreview.SetFlag(WidgetFlags.NoWindow);
413
propertyBox = new PropertyBox(this);
415
outerBox = new HBox(false, 12);
416
outerBox.BorderWidth = 6;
417
outerBox.PackStart(itemPreview, false, false, 0);
418
outerBox.PackStart(propertyBox, true, true, 0);
420
// (Gtk.Frame derives from Gtk.Bin which has no window)
421
Frame frame = new Frame();
425
// setting the no_window flag on current windows GTK seems to be buggy.
426
// remove no_window flag and use a solid bg color instead of a gradient.
427
// check later if the flag is working.
428
if (Platform.Common.Diagnostics.CurrentPlatform.IsWin32) {
429
Gdk.Color baseColor = Style.Base(StateType.Normal);
431
itemPreview.Flags = itemPreview.Flags ^ (int)WidgetFlags.NoWindow;
432
itemPreview.ModifyBg(StateType.Normal, baseColor);
434
EventBox eb = new EventBox();
435
eb.ModifyBg(StateType.Normal, baseColor);
442
frame.ExposeEvent += OnExposeEvent;
446
[GLib.ConnectBefore()]
447
private void OnExposeEvent (object o, ExposeEventArgs args) {
448
int x = args.Event.Area.X;
449
int y = args.Event.Area.Y;
450
int width = args.Event.Area.Width;
451
int height = args.Event.Area.Height;
453
Gdk.Color startColor = Style.Mid(StateType.Normal);
454
Gdk.Color stopColor = Style.Light(StateType.Normal);
457
using (Context cr = Gdk.CairoHelper.Create(args.Event.Window)) {
459
cr.Rectangle(x, y, width, height);
461
Gradient pat = new LinearGradient(x, y, x, y + height / 1.2);
462
pat.AddColorStop(0, ToCairoColor(startColor, alpha));
463
pat.AddColorStop(1, ToCairoColor(stopColor, alpha));
470
private static Cairo.Color ToCairoColor(Gdk.Color c, double alpha) {
471
double gdk_max = (double)ushort.MaxValue;
472
return new Cairo.Color(c.Red / gdk_max,
478
#region PropertyBox class
479
private class PropertyBox : VBox
481
private Label lblName;
482
private Arrow btnArrow;
483
private Table[] tbls;
485
private Label[] captionLbls;
486
private Label[] valueLbls;
487
private ItemInfo owner;
489
private const int MAX_ITEM_PROPERTIES = 8; // must be a multiple of 2
490
private const int WIDTH = 2;
491
private const int HEIGHT = MAX_ITEM_PROPERTIES / WIDTH;
493
public PropertyBox(ItemInfo owner) : base(false, 12) {
496
this.captionLbls = new Label[MAX_ITEM_PROPERTIES];
497
this.valueLbls = new Label[MAX_ITEM_PROPERTIES];
499
this.lblName = WindowBase.CreateLabel(string.Empty, true);
500
this.lblName.Ellipsize = Pango.EllipsizeMode.End;
502
this.tbls = new Table[WIDTH];
503
this.hbox = new HBox(false, 12);
505
for (int i = 0; i < WIDTH; i++) {
506
this.tbls[i] = WindowBase.CreateTable(HEIGHT, 2, 6); // 2 = caption + value
507
this.hbox.PackStart(tbls[i], true, true, 0);
511
for (int i = 0; i < MAX_ITEM_PROPERTIES; i++, y++) {
518
// create caption label
519
this.captionLbls[i] = WindowBase.CreateLabel(string.Empty, true);
521
// create value label
522
this.valueLbls[i] = WindowBase.CreateLabel(string.Empty, false);
523
this.valueLbls[i].Ellipsize = Pango.EllipsizeMode.End;
525
// attach caption and value labels to the table
526
WindowBase.TblAttach(tbls[tbl], captionLbls[i], 0, y);
527
this.tbls[tbl].Attach(valueLbls[i], 1, 2, (uint)y, (uint)(y + 1));
531
HBox hb = new HBox(false, 6);
532
this.btnArrow = new Arrow(ArrowType.Down, ShadowType.None);
533
hb.PackStart(btnArrow, false, false, 0);
534
hb.PackStart(lblName, true, true, 0);
536
Button btn = new Button(hb);
537
btn.Clicked += OnBtnClicked;
539
this.PackStart(btn, true, false, 0);
540
this.PackStart(hbox, true, true, 0);
543
public void SetNameProperty(string name, string tooltip) {
544
lblName.LabelProp = name;
545
lblName.TooltipText = tooltip;
548
public void SetProperties(ItemProperty[] properties) {
549
int itemCount = (properties.Length < MAX_ITEM_PROPERTIES) ? properties.Length : MAX_ITEM_PROPERTIES;
551
// hide second table when it won't contain labels
552
tbls[1].Visible = (itemCount > HEIGHT);
554
for (int i = 0; i < itemCount; i++) {
555
ItemProperty p = properties[i];
557
captionLbls[i].LabelProp = string.Format("<b>{0}:</b>", p.name);
560
valueLbls[i].LabelProp = p.value.Replace('\n', ' ').Replace('\r', ' ');
561
valueLbls[i].TooltipText = p.value;
564
// clear remaining labels
565
for (int i = itemCount; i < MAX_ITEM_PROPERTIES; i++) {
566
captionLbls[i].LabelProp = string.Empty;
568
valueLbls[i].LabelProp = string.Empty;
569
valueLbls[i].TooltipText = string.Empty;
573
public void Clear() {
574
SetNameProperty(string.Empty, string.Empty);
575
for (int i = 0; i < MAX_ITEM_PROPERTIES; i++) {
576
captionLbls[i].LabelProp = String.Empty;
577
valueLbls[i].LabelProp = valueLbls[i].TooltipText = String.Empty;
581
public bool Minimized {
583
return !hbox.Visible;
587
btnArrow.ArrowType = ArrowType.Right;
588
hbox.Visible = false;
590
btnArrow.ArrowType = ArrowType.Down;
594
if (owner.Minimized != value)
595
owner.Minimized = value;
599
private void OnBtnClicked(object sender, EventArgs args) {
600
Minimized = !Minimized;