~ubuntu-branches/ubuntu/wily/bombono-dvd/wily

« back to all changes in this revision

Viewing changes to src/mgui/project/mb-actions.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessio Treglia
  • Date: 2010-11-04 11:46:25 UTC
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20101104114625-8xfdhvhpsm51i0nu
Tags: upstream-0.8.0
Import upstream version 0.8.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
#include <mgui/_pc_.h>
23
23
 
24
24
#include "mb-actions.h"
 
25
#include "add.h"
 
26
 
25
27
#include "handler.h"
26
28
#include "thumbnail.h"
27
 
#include "mconstructor.h" // APROJECT_NAME
28
 
#include "mconstructor.h" // APROJECT_NAME
29
29
 
30
 
#include <mbase/project/table.h>
31
 
#include <mgui/timeline/service.h>
 
30
#include <mgui/timeline/dvdmark.h>
 
31
#include <mgui/timeline/mviewer.h>
32
32
#include <mgui/trackwindow.h>
33
33
#include <mgui/img-factory.h>
34
 
#include <mgui/dialog.h>
35
 
#include <mgui/sdk/window.h>
36
 
#include <mgui/gettext.h>
 
34
#include <mgui/prefs.h>
37
35
 
 
36
#include <mbase/project/table.h>
38
37
#include <gtk/gtktreestore.h>
39
 
#include <mlib/sdk/logger.h>
40
 
#include <mlib/filesystem.h>
41
 
 
42
 
#include <boost/lexical_cast.hpp>
43
 
#include <boost/regex.hpp>
44
 
#include <strings.h> // strcasecmp()
45
 
 
46
 
#include <gtk/gtkversion.h>
47
38
 
48
39
namespace Project
49
40
{
68
59
 
69
60
void FillThumbnail(const Gtk::TreeIter& itr, RefPtr<MediaStore> ms, Media& md)
70
61
{
71
 
    RefPtr<Gdk::Pixbuf> thumb_pix = itr->get_value(ms->columns.thumbnail);
 
62
    Gtk::TreeModelColumn<RefPtr<Gdk::Pixbuf> > thumb_cln = MediaStore::Fields().thumbnail;
 
63
    RefPtr<Gdk::Pixbuf> thumb_pix = itr->get_value(thumb_cln);
72
64
    if( !thumb_pix )
73
65
    {
74
66
        // * серый фон
76
68
        thumb_pix = CreatePixbuf(thumb_sz);
77
69
        thumb_pix->fill(RGBA::ToUint(Gdk::Color("light grey")));
78
70
 
79
 
        itr->set_value(ms->columns.thumbnail, thumb_pix);
 
71
        itr->set_value(thumb_cln, thumb_pix);
80
72
    }
81
73
 
82
74
    Point thumb_sz(PixbufSize(thumb_pix));
126
118
void PublishMedia(const Gtk::TreeIter& itr, RefPtr<MediaStore> ms, MediaItem mi)
127
119
{
128
120
    FillThumbnail(itr, ms, *mi);
129
 
    (*itr)[ms->columns.media] = mi;
 
121
    (*itr)[MediaStore::Fields().media] = mi;
130
122
 
131
123
    PublishMediaVis vis(itr, ms);
132
124
    mi->Accept(vis);
133
125
}
134
126
 
135
 
static std::string MarkError(const std::string& val, bool not_error)
136
 
{
137
 
    if( not_error )
138
 
        return val;
139
 
    return "<span foreground=\"red\">" + val + "</span>";
140
 
}
141
 
 
142
 
static std::string MarkBoolError(bool val, bool not_error)
143
 
{
144
 
    return MarkError(std::string(val ? _("yes") : _("no")), not_error);
145
 
}
146
 
 
147
 
 
148
 
namespace { 
149
 
 
150
 
struct ErrorDesc
151
 
{
152
 
           bool  res;
153
 
    std::string  outStr;
154
 
    std::string  descStr;
155
 
    
156
 
    ErrorDesc(): res(true) {}
157
 
};
158
 
 
159
 
static void SetImportError(ErrorDesc& ed, bool is_good, const std::string& out_str, 
160
 
                           const std::string& desc_str = std::string())
161
 
{
162
 
    if( !is_good && ed.res )
163
 
    {
164
 
        ed.res = false;
165
 
        ed.descStr = desc_str; // внимание на первую ошибку
166
 
    }
167
 
 
168
 
    #define M_SPS "\n   "
169
 
    ed.outStr += M_SPS + out_str;
170
 
    #undef M_SPS
171
 
}
172
 
 
173
 
static std::string FpsToStr(const Point& frate)
174
 
{
175
 
    return (str::stream() << (double)frate.x/frate.y).str();
176
 
}
177
 
 
178
 
static std::string TVTypeStr(bool is_ntsc)
179
 
{
180
 
    return std::string(is_ntsc ? "NTSC" : "PAL/SECAM");
181
 
}
182
 
 
183
 
void CheckVideoFormat(ErrorDesc& ed, const Mpeg::SequenceData& vid, bool is_ntsc)
184
 
{
185
 
    using namespace boost;
186
 
    //
187
 
    // Doc: DVD-Video/1. Mpucoder Specs/DVD/dvdmpeg.html
188
 
    // проверка подходимости для DVD
189
 
    // *
190
 
    int kbps = vid.bytRat/400;
191
 
    bool is_byte_rate_ok = kbps <= 9800; // Kbps
192
 
    SetImportError(ed, is_byte_rate_ok, 
193
 
                   std::string(_("Video bitrate")) + ":\t" + 
194
 
                   MarkError(lexical_cast<std::string>(kbps), is_byte_rate_ok) + " " + _("kbps"), 
195
 
                   _("Maximum data rate for video (9800 kbps) is exceeded."));
196
 
 
197
 
    const char* Descriptions[] = {
198
 
        N_("The %1% DVD-Video can accept MPEG-2 with resolutions: %2% only."),
199
 
        N_("The %1% DVD-Video can accept MPEG-2 with frame rate: %2% only."),
200
 
        N_("The %1% DVD-Video can accept MPEG-2 with aspects 4:3, 16:9 only.")
201
 
    };
202
 
    std::string tv_type = TVTypeStr(is_ntsc);
203
 
 
204
 
 
205
 
    // * (352x480, 352x288 - не по стандарту, см. Trac#9)
206
 
    Point ntsc_resolutions[] = { Point(720, 480), Point(704, 480), Point(352, 480), Point(352, 240) };
207
 
    Point pal_resolutions[]  = { Point(720, 576), Point(704, 576), Point(352, 576), Point(352, 288) };
208
 
    Point sz(vid.wdh, vid.hgt);
209
 
    bool sz_ok = false;
210
 
    std::string resol_list;
211
 
    Point* resolutions = is_ntsc ? ntsc_resolutions : pal_resolutions ;
212
 
    for( int i=0; i<(int)ARR_SIZE(ntsc_resolutions); i++ )
213
 
    {
214
 
        sz_ok = sz_ok || sz == resolutions[i];
215
 
        if( !resol_list.empty() )
216
 
            resol_list += ", ";
217
 
        resol_list += PointToStr(resolutions[i]);
218
 
    }   
219
 
    SetImportError(ed, sz_ok, 
220
 
                   std::string(_("Video size")) + ":   \t" + MarkError(PointToStr(sz), sz_ok),
221
 
                   BF_(Descriptions[0]) % tv_type % resol_list % bf::stop);
222
 
 
223
 
    // *
224
 
    Point frate(vid.framRat);
225
 
    // прогрессивность не учитываем, потому что на DVD Demystified(VTS_18_1.VOB) не сработало
226
 
    //bool is_progr = vid.isProgr;
227
 
    bool frate_ok = false;
228
 
    std::string frate_list;
229
 
    if( is_ntsc )
230
 
    {
231
 
        frate_ok = frate == Point(24, 1) || frate == Point(30000, 1001);
232
 
        frate_list = FpsToStr(Point(24, 1)) + ", " + FpsToStr(Point(30000, 1001));
233
 
    }
234
 
    else
235
 
    {
236
 
        frate_ok   = frate == Point(25, 1);
237
 
        frate_list = FpsToStr(Point(25, 1));
238
 
    }
239
 
    SetImportError(ed, frate_ok, 
240
 
                   std::string(_("Frame rate")) + ":   \t" + 
241
 
                   MarkError(FpsToStr(frate), frate_ok) + " " + _("fps"),
242
 
                   BF_(Descriptions[1]) % tv_type % frate_list % bf::stop);
243
 
 
244
 
    // *
245
 
    bool is_aspect_ok = vid.sarCode == af4_3 || vid.sarCode == af16_9;
246
 
    Point aspect = vid.SizeAspect();
247
 
    std::string aspect_str = (str::stream() << aspect.x << ':' << aspect.y).str();
248
 
    SetImportError(ed, is_aspect_ok, 
249
 
                   std::string(_("Aspect ratio")) + ": \t" + MarkError(aspect_str, is_aspect_ok),
250
 
                   BF_(Descriptions[2]) % tv_type % bf::stop);
251
 
 
252
 
    // * 
253
 
    bool no_delay = !vid.lowDelay;
254
 
    // Translators: Low delay is very tech term and can be left as is.
255
 
    SetImportError(ed, no_delay, "\"Low delay\":    \t" + MarkBoolError(!no_delay, no_delay));
256
 
    ed.outStr += "\n";
257
 
}
258
 
 
259
 
} // namespace 
260
 
 
261
 
bool CanOpenAsVideo(const char* fname, std::string& err_string, bool& must_be_video)
262
 
{
263
 
    Mpeg::PlayerData pd;
264
 
    io::stream& strm     = pd.srcStrm;
265
 
    Mpeg::MediaInfo& inf = pd.mInf;
266
 
 
267
 
    bool res = false;
268
 
    if( !Mpeg::GetInfo(pd, fname) )
269
 
        err_string = inf.ErrorReason();
270
 
    else
271
 
    {
272
 
        must_be_video = true; // дальше не проверяем тип - это точно видео
273
 
        Mpeg::SequenceData& vid = inf.vidSeq;
274
 
        ErrorDesc ed;
275
 
        // * видео
276
 
        bool is_ntsc = !AData().PalTvSystem();
277
 
        CheckVideoFormat(ed, vid, is_ntsc);
278
 
        bool video_check = ed.res;
279
 
 
280
 
        // * мультиплексирование в формате DVD
281
 
        bool is_dvd_mux   = false;
282
 
        bool is_nav_found = false;
283
 
        const int pack_cnt = 8; // первые 8*2048=16Kb проверяем
284
 
        if( inf.endPos >= pack_cnt*DVD_PACK_SZ )
285
 
        {
286
 
            uint8_t buf[DVD_PACK_SZ];
287
 
            strm.seekg(0);
288
 
 
289
 
            is_dvd_mux = true;
290
 
            for( int i=0; i<pack_cnt && strm.read((char*)buf, DVD_PACK_SZ); )
291
 
            {
292
 
                if( !(buf[0] == 0 && buf[1] == 0 && buf[2] == 1 && buf[2] && 0xba) )
293
 
                {
294
 
                    is_dvd_mux = false;    
295
 
                    break;
296
 
                }
297
 
 
298
 
                //
299
 
                // COPY_N_PASTE_ETALON из dvdvob.c, http://dvdauthor.sourceforge.net/
300
 
                // 
301
 
                // Замечание - из-за недоступности стандарта DVD-VIDEO для нас стандарт - dvdauthor!
302
 
                // Более того, судя по первому замечанию в TODO для версии 0.6.14, - размер system header
303
 
                // может быть не равен 18 байтам (но сути это не меняет - должны принимать только то, что
304
 
                // сможет принять dvdauthor)
305
 
                if( buf[14] == 0 &&
306
 
                    buf[15] == 0 &&
307
 
                    buf[16] == 1 &&
308
 
                    buf[17] == 0xbb ) // system header
309
 
                {
310
 
                    if( buf[38] == 0 && buf[39] == 0 && buf[40] == 1 && buf[41] == 0xbf && // 1st private2
311
 
                        buf[1024] == 0 && buf[1025] == 0 && buf[1026] == 1 && buf[1027] == 0xbf ) // 2nd private2
312
 
                        is_nav_found = true;
313
 
                }
314
 
 
315
 
                        // пропускаем спец. pad-данные самого dvdauthor - не считаются 
316
 
                        // (добавляются при деавторинге, добавлении субтитров)
317
 
                if ( buf[14] == 0 && buf[15] == 0 && buf[16] == 1 && buf[17] == 0xbe && 
318
 
                     strcmp((char*)buf+20,"dvdauthor-data") == 0 )
319
 
                    ;
320
 
                else
321
 
                    i++;
322
 
            }
323
 
        }
324
 
        std::string dvd_mux_desc = _("<b>Bombono DVD</b> can use \"DVD-ready\" video only now."
325
 
            " Use muxing programs like \"mplex -f 8\" (from <b>mjpegtools</b>)," 
326
 
            " mencoder (from <b>mplayer</b>) or <b>transcode</b> to make your video ready for <b>Bombono DVD</b>.");
327
 
        SetImportError(ed, is_dvd_mux,
328
 
                       boost::format("%1%:    \t") % _("DVD packs") % bf::stop + MarkBoolError(is_dvd_mux, is_dvd_mux), dvd_mux_desc);
329
 
        SetImportError(ed, is_nav_found, 
330
 
                       boost::format("%1%:  \t") % _("NAV packets") % bf::stop + MarkBoolError(is_nav_found, is_nav_found), dvd_mux_desc);
331
 
        bool dvd_check = is_dvd_mux && is_nav_found;
332
 
 
333
 
        res = ed.res;
334
 
        if( !res )
335
 
        {
336
 
            err_string  = _("This video may not be added due to (errors in <span foreground=\"red\">red color</span>):");
337
 
            err_string += "\n<tt>" + ed.outStr + "</tt>";
338
 
 
339
 
            std::string desc_str = ed.descStr;
340
 
            if( !video_check && dvd_check )
341
 
            {
342
 
                ErrorDesc ed2;
343
 
                CheckVideoFormat(ed2, vid, !is_ntsc);
344
 
                if( ed2.res )
345
 
                {
346
 
                    // подскажем пользователю, что он ошибся форматом проекта
347
 
                    desc_str = BF_("This video has %1% type and can't be added to"
348
 
                        " current project of %2% type. Create new project from"
349
 
                        " menu \"Project->New Project\" with right type.") % 
350
 
                        TVTypeStr(!is_ntsc) % TVTypeStr(is_ntsc) % bf::stop;
351
 
                }
352
 
            }
353
 
 
354
 
            if( !desc_str.empty() )
355
 
                err_string += "\n\n" + desc_str;
356
 
        }
357
 
    }
358
 
 
359
 
    return res;
360
 
}
361
 
 
362
 
inline bool CaseIEqual(const std::string &s1, const std::string &s2)
363
 
{
364
 
    return strcasecmp(s1.c_str(), s2.c_str()) == 0;
365
 
}
366
 
 
367
 
StorageItem CreateMedia(const char* fname, std::string& err_string)
368
 
{
369
 
    StorageItem md;
370
 
 
371
 
    // 1 общая проверка
372
 
    fs::path pth(fname);
373
 
    if( !fs::exists(pth) )
374
 
    {
375
 
        err_string = _("File doesn't exist.");
376
 
        return md;
377
 
    }
378
 
    if( fs::is_directory(pth) )
379
 
    {
380
 
        err_string = _("Folders can't be added.");
381
 
        return md;
382
 
    }
383
 
 
384
 
    //
385
 
    // 2 определение типа файла
386
 
    // Пока открываем только минимальное кол-во
387
 
    // типов медиа (картинки и MPEG2), потому простой
388
 
    // перебор
389
 
    // :TODO: переделать; с помощью библиотеки libmagick
390
 
    // (утилита '/usr/bin/file') реализовать первичное 
391
 
    // определение типа файла
392
 
    //
393
 
 
394
 
    std::string fext = get_extension(pth);
395
 
    bool must_be_video = CaseIEqual(fext, "mpeg") || CaseIEqual(fext, "mpg") || 
396
 
                         CaseIEqual(fext, "m2v")  || CaseIEqual(fext, "vob");
397
 
    int wdh, hgt;
398
 
    std::string video_err_str;
399
 
    if( CanOpenAsVideo(fname, video_err_str, must_be_video) )
400
 
    {
401
 
        // 2.1 видео
402
 
        md = new VideoMD;
403
 
        md->MakeByPath(fname);
404
 
    }
405
 
    else if( !must_be_video && gdk_pixbuf_get_file_info(fname, &wdh, &hgt) )
406
 
    {
407
 
        // 2.2 картинка
408
 
        md = new StillImageMD;
409
 
        md->MakeByPath(fname);
410
 
    }
411
 
 
412
 
    if( !md )
413
 
    {
414
 
        // по расширению выводим наиболее вероятную ошибку
415
 
        err_string  = must_be_video ? video_err_str : _("Unknown file type.") ;
416
 
    }
417
 
    return md;
418
 
}
419
 
 
420
127
static void OnVideoView(TrackLayout& layout, VideoMD* vd, int chp_pos)
421
128
{
422
129
    using namespace Timeline;
436
143
        layout.SetPos(GetMarkData(chp_pos).pos);
437
144
}
438
145
 
439
 
static ViewMediaVis::Fnr MakeViewFnr(VideoItem vi, int chp_pos)
 
146
class ViewMediaVis: public ObjVisitor
 
147
{
 
148
    public:
 
149
            BoolTLFunctor  vFnr;
 
150
 
 
151
    protected:
 
152
 
 
153
            void  Visit(VideoMD& obj);
 
154
            void  Visit(VideoChapterMD& obj);
 
155
};
 
156
 
 
157
BoolTLFunctor GetViewerFunctor(MediaItem mi)
 
158
{
 
159
    ViewMediaVis vis;
 
160
    mi->Accept(vis);
 
161
    return vis.vFnr;
 
162
}
 
163
 
 
164
static BoolTLFunctor MakeViewFnr(VideoItem vi, int chp_pos)
440
165
{
441
166
    using namespace boost;
442
 
    TLFunctor a_fnr = lambda::bind(&OnVideoView, lambda::_1, vi.get(), chp_pos);
443
 
    return lambda::bind(&OpenTrackLayout, lambda::_1, vi, a_fnr);
 
167
    TLFunctor a_fnr = bb::bind(&OnVideoView, _1, vi.get(), chp_pos);
 
168
    return bb::bind(&OpenTrackLayout, _1, vi, a_fnr);
444
169
}
445
170
 
446
171
void ViewMediaVis::Visit(VideoMD& obj)
456
181
 
457
182
void ViewMedia(TrackLayout& layout, MediaItem mi)
458
183
{
459
 
    ViewMediaVis::Fnr fnr = ViewMediaVis::GetViewerFunctor(mi);
 
184
    BoolTLFunctor fnr = GetViewerFunctor(mi);
460
185
    if( fnr && fnr(layout) )
461
186
        GrabFocus(layout);
462
187
}
579
304
        PublishMedia(ms->append(), ms, *itr);
580
305
}
581
306
 
582
 
bool TryAddMedia(const char* fname, Gtk::TreePath& pth, std::string& err_str,
583
 
                 bool insert_after)
584
 
{
585
 
    bool res = false;
586
 
    if( StorageItem md = CreateMedia(fname, err_str) )
587
 
    {
588
 
        res = true;
589
 
        LOG_INF << "Insert Media!" << io::endl;
590
 
 
591
 
        RefPtr<MediaStore> ms = GetAStores().mdStore;
592
 
        Gtk::TreeIter itr = InsertByPos(ms, pth, insert_after);
593
 
        PublishMedia(itr, ms, md);
594
 
        InvokeOnInsert(md);
595
 
 
596
 
        pth = ms->get_path(itr);
597
 
    }
598
 
    return res;
599
 
}
600
 
 
601
 
// desc - метка происхождения, добавления
602
 
void TryAddMediaQuiet(const std::string& fname, const std::string& desc)
603
 
{
604
 
    std::string err_str;
605
 
    Gtk::TreePath pth;
606
 
    if( !TryAddMedia(fname.c_str(), pth, err_str) )
607
 
    {    
608
 
        LOG_ERR << "TryAddMediaQuiet error (" << desc << "): " << err_str << io::endl;
609
 
    }
610
 
}
611
 
 
612
 
static std::string StandFNameOut(const fs::path& pth)
613
 
{
614
 
    return "<span style=\"italic\" underline=\"low\">" + 
615
 
                    pth.leaf() + "</span>";
616
 
}
617
 
 
618
 
#if GTK_CHECK_VERSION(2,18,0)
619
 
#define LABEL_HAS_A_TAG
620
 
#endif
621
 
 
622
 
static void AddMediaError(const std::string& msg_str, const std::string& desc_str)
623
 
{
624
 
#ifdef LABEL_HAS_A_TAG
625
 
    MessageBoxWeb(msg_str, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, desc_str);
626
 
#else
627
 
    MessageBox(msg_str, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, desc_str);
628
 
#endif
629
 
}
630
 
 
631
 
void TryAddMedias(const Str::List& paths, MediaBrowser& brw,
632
 
                  Gtk::TreePath& brw_pth, bool insert_after)
633
 
{
634
 
    // * подсказка с импортом
635
 
    if( paths.size() )
636
 
    {
637
 
        const std::string fname = paths[0];
638
 
        fs::path pth(fname); 
639
 
        std::string leaf = pth.leaf();
640
 
        {
641
 
            std::string::const_iterator start = leaf.begin(), end = leaf.end();
642
 
            static boost::regex dvd_video_vob("(VIDEO_TS|VTS_[0-9][0-9]_[0-9]).VOB", 
643
 
                                              boost::regex::perl|boost::regex::icase);
644
 
    
645
 
            if( boost::regex_match(start, end, dvd_video_vob) && 
646
 
                MessageBox(BF_("The file \"%1%\" looks like VOB from DVD.\nRun import?") % leaf % bf::stop,
647
 
                           Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL) == Gtk::RESPONSE_OK )
648
 
            {
649
 
                DVD::RunImport(*GetTopWindow(brw), pth.branch_path().string());
650
 
                return;
651
 
            }
652
 
        }
653
 
 
654
 
        std::string ext = get_extension(pth);
655
 
        const char* el_array[] = {"m2v", "mp2", "mpa", "ac3", "dts", "lpcm", 0 };
656
 
        bool res = false;
657
 
        for( const char** el = el_array; *el ; el++ )
658
 
            if( *el == ext )
659
 
            {
660
 
                res = true;
661
 
                break;
662
 
            }
663
 
        if( res && MessageBox(BF_("The file \"%1%\" looks like elementary stream and need to be muxed before using. Run muxing?") % leaf % bf::stop,
664
 
                              Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL) == Gtk::RESPONSE_OK )
665
 
        {
666
 
            MuxAddStreams(fname);
667
 
            return;
668
 
        }
669
 
    }
670
 
 
671
 
    // когда одна ошибка
672
 
    std::string err_str;
673
 
    fs::path err_pth;
674
 
    int err_cnt = 0;
675
 
    // куда переходим в браузере
676
 
    Gtk::TreePath goto_path;
677
 
    std::string err_desc;
678
 
    for( Str::List::const_iterator itr = paths.begin(), end = paths.end(); 
679
 
         itr != end; ++itr )
680
 
    {
681
 
        const std::string& fpath = *itr;
682
 
        std::string ConvertPathToUtf8(const std::string& path);
683
 
        fs::path pth = ConvertPathToUtf8(fpath);
684
 
 
685
 
        bool is_exist = false;
686
 
        // * проверяем, есть ли такой уже
687
 
        RefPtr<MediaStore> ms = brw.GetMediaStore();
688
 
        for( MediaStore::iterator itr = ms->children().begin(), end = ms->children().end();
689
 
             itr != end; ++itr )
690
 
        {
691
 
            StorageItem si = GetAsStorage(ms->GetMedia(itr));
692
 
            if( fs::equivalent(pth, si->GetPath()) )
693
 
            {
694
 
                // только переходим к нему
695
 
                brw.get_selection()->select(GetBrowserPath(si));
696
 
                is_exist = true;
697
 
                break;
698
 
            }
699
 
        }
700
 
        if( is_exist )
701
 
            continue;
702
 
 
703
 
        bool res = TryAddMedia(fpath.c_str(), brw_pth, err_str, insert_after);
704
 
        if( res )
705
 
        {
706
 
            insert_after = true; // вставляем друг за другом
707
 
            goto_path    = brw_pth;
708
 
        }
709
 
        else
710
 
        {
711
 
            err_pth = pth;
712
 
            err_cnt++;
713
 
 
714
 
            const int max_show_errors = 2+1;
715
 
            if( err_cnt < max_show_errors )
716
 
            {
717
 
                if( !err_desc.empty() )
718
 
                    err_desc += "\n\n";
719
 
                err_desc += StandFNameOut(pth);
720
 
                if( !err_str.empty() )
721
 
                    err_desc += "\n" + err_str;
722
 
            }
723
 
            else
724
 
            {
725
 
                err_desc += (err_cnt == max_show_errors) ? std::string("\n\n") + _("Also:") + " " : std::string(", ") ;
726
 
                err_desc += StandFNameOut(pth);
727
 
            }
728
 
        }
729
 
    }
730
 
 
731
 
    if( err_cnt )
732
 
    {
733
 
        bool one_error = (err_cnt == 1);
734
 
        std::string desc = one_error ? err_str : err_desc ;
735
 
 
736
 
#ifdef LABEL_HAS_A_TAG
737
 
        std::string online_tip("\n\n");
738
 
        online_tip += BF_("See more about preparing video for authoring in <a href=\"%1%\">online help</a>.") % 
739
 
            "http://www.bombono.org/Preparing_sources_for_DVD" % bf::stop;
740
 
 
741
 
        desc += online_tip;
742
 
#endif
743
 
 
744
 
        // :KLUDGE: хотелось использовать ngettext() для того чтоб в PO строки были рядом,
745
 
        // однако msgfmt для требует чтобы в обоих вариантах присутствовало одинаковое 
746
 
        // кол-во заполнителей 
747
 
        //boost::format frmt(ngettext("Can't add file \"%1%\".", "Can't add files:", err_cnt));
748
 
        if( one_error )
749
 
            AddMediaError(BF_("Can't add file \"%1%\".") % err_pth.leaf() % bf::stop, desc);
750
 
        else
751
 
            AddMediaError(_("Can't add files:"), desc);
752
 
    }
753
 
 
754
 
    if( !goto_path.empty() )
755
 
        GoToPos(brw, goto_path);
756
 
}
757
 
 
758
 
Str::List GetFilenames(Gtk::FileChooser& fc)
759
 
{
760
 
    // я знаю, что это жутко неэффективно (2+1 копирования), но не актуально
761
 
    typedef Glib::SListHandle<Glib::ustring> UList;
762
 
    UList ulist = fc.get_filenames();
763
 
 
764
 
    Str::List list;
765
 
    std::copy(ulist.begin(), ulist.end(), std::back_inserter(list));
766
 
    return list;
767
 
}
768
 
 
769
 
static void MediaBrowserAdd(MediaBrowser& brw, const char* /*fname*/,
770
 
                            Gtk::FileChooser& fc)
771
 
{
772
 
    Str::List paths(GetFilenames(fc));
773
 
    // * куда
774
 
    Gtk::TreePath brw_pth = GetCursor(brw);
775
 
    ValidateMediaInsertionPos(brw_pth);
776
 
 
777
 
    TryAddMedias(paths, brw, brw_pth, true);
778
 
}
779
 
 
780
307
void OnBrowserRowActivated(MediaBrowser& brw, MediaActionFnr fnr, const Gtk::TreeModel::Path& path)
781
308
{
782
309
    RefPtr<MediaStore> ms = brw.GetMediaStore();
788
315
                  MediaBrowser& brw)
789
316
{
790
317
    ActionFunctor open_fnr = 
791
 
        PackFileChooserWidget(fcw_hpaned, bl::bind(&MediaBrowserAdd, boost::ref(brw), bl::_1, bl::_2), false);
 
318
        PackFileChooserWidget(fcw_hpaned, bb::bind(&MediaBrowserAdd, boost::ref(brw), _2), false);
792
319
 
793
320
    Gtk::HPaned& hpaned = *Gtk::manage(new Gtk::HPaned);
794
 
    hpaned.set_position(BROWSER_WDH);
 
321
    SetUpdatePos(hpaned, UnnamedPrefs().mdBrw1Wdh);
795
322
    fcw_hpaned.add2(hpaned);
796
323
 
797
324
    // *
798
 
    MediaActionFnr view_fnr = bl::bind(&ViewMedia, boost::ref(layout), bl::_1);
 
325
    MediaActionFnr view_fnr = bb::bind(&ViewMedia, boost::ref(layout), _1);
799
326
    PackMediaBrowserAll(PackAlignedForBrowserTB(hpaned), brw, open_fnr, 
800
 
                        bl::bind(&DeleteMediaFromBrowser, boost::ref(brw)),
801
 
                        bl::bind(&ExecuteForMedia, boost::ref(brw), view_fnr));
 
327
                        bb::bind(&DeleteMediaFromBrowser, boost::ref(brw)),
 
328
                        bb::bind(&ExecuteForMedia, boost::ref(brw), view_fnr));
802
329
    brw.signal_row_activated().connect( 
803
 
       bl::bind(&OnBrowserRowActivated, boost::ref(brw), view_fnr, bl::_1) );
 
330
       bb::bind(&OnBrowserRowActivated, boost::ref(brw), view_fnr, _1) );
804
331
 
805
332
    // *
806
333
    hpaned.add2(PackMonitorIn(mon));