<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2016 Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
Lesser General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with Abaclade. If
not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#include <abaclade.hxx>
#include <abaclade/collections/vector.hxx>
#include <abaclade/text/parsers/dynamic.hxx>

////////////////////////////////////////////////////////////////////////////////////////////////////

namespace abc { namespace text { namespace parsers {

dynamic::state::state() {
   pstNext = nullptr;
   pstAlternative = nullptr;
   u.range.cpFirst = '\0';
   u.range.cpLast   = '\0';
   u.repetition.pstRepeated = nullptr;
   u.repetition.cMin = 0;
   u.repetition.cMax = 0;
   st = state_type::range;
}

struct dynamic::backtrack {
   backtrack(state_t const * pstAlternative_, bool bConsumedCp_) :
      pstAlternative(pstAlternative_),
      bConsumedCp(bConsumedCp_) {
   }

   state_t const * pstAlternative;
   bool bConsumedCp;
};

struct dynamic::repetition {
   explicit repetition(state_t const * pmnAnchor_) :
      pmnAnchor(pmnAnchor_),
      c(0),
      bBacktracking(false) {
   }

   state_t const * pmnAnchor;
   std::uint16_t c;
   bool bBacktracking:1;
};

dynamic::dynamic() :
   m_pstInitial(nullptr) {
}

dynamic::dynamic(dynamic && dp) :
   m_qmn(_std::move(dp.m_qmn)),
   m_pstInitial(dp.m_pstInitial) {
   dp.m_pstInitial = nullptr;
}

dynamic::~dynamic() {
}

dynamic::state * dynamic::create_state() {
   m_qmn.push_back(state_t());
   return static_cast<state *>(&m_qmn.back());
}

bool dynamic::run(str const & s) const {
   io::text::str_istream sis(external_buffer, &s);
   return run(&sis);
}

bool dynamic::run(io::text::istream * ptis) const {
   state_t const * pstCurr = m_pstInitial;
   // Setup the two sources of code points: a backtrack and a peek buffer from the input stream.
   str sPeek = ptis->peek_chars(1);
   auto itPeek(sPeek.cbegin()), itPeekEnd(sPeek.cend());
   str sHistory;
   auto itHistory(sHistory.cbegin()), itHistoryEnd(sHistory.cend());

   collections::vector<backtrack> vbtStack;
   collections::vector<repetition> vrepStack;

   // The first code point must necessarily come from sPeek, since sHistory is empty.
   bool bAccepted = false;
   while (pstCurr) {
      state_t const * pstNext = nullptr;
      bool bConsumedCp = false;
      switch (pstCurr->st) {
         case state_type::range: {
            // Get a code point from either history or the peek buffer.
            char32_t cp;
            bool bSaveCpToHistory;
            if (itHistory != itHistoryEnd) {
               cp = *itHistory;
               bSaveCpToHistory = false;
            } else {
               if (itPeek == itPeekEnd) {
                  ptis->consume_chars(sPeek.size_in_chars());
                  sPeek = ptis->peek_chars(1);
                  itPeek = sPeek.cbegin();
                  itPeekEnd = sPeek.cend();
                  if (itPeek == itPeekEnd) {
                     // Run out of code points.
                     break;
                  }
               }
               cp = *itPeek;
               bSaveCpToHistory = true;
            }

            if (cp >= pstCurr->u.range.cpFirst && cp <= pstCurr->u.range.cpLast) {
               bAccepted = true;
               pstNext = pstCurr->pstNext;
               if (bSaveCpToHistory) {
                  sHistory += cp;
                  ++itHistoryEnd;
               }
               ++itHistory;
               bConsumedCp = true;
            }
            break;
         }

         case state_type::repetition:
            /* TODO: this behavior is greedy; add support for non-greedy behavior, i.e. trying to
            match the least number of occurrences first. */
            if (vrepStack && vrepStack.front().pmnAnchor == pstCurr) {
               /* We just got back to the repetition at the top of the stack. Check if we should try
               to match it once more. */
               repetition & rep = vrepStack.front();
               if (rep.bBacktracking) {
                  if (rep.c-- == pstCurr->u.repetition.cMin) {
                     /* Can’t match this repetition any fewer times; this means that the repetition
                     can’t be matched. */
                     ;
                  } else {
                     /* We already know that the repetition can be matched one fewer time, and the
                     stacks already account for that; just skip straight to out of it. */
                     bAccepted = true;
                     pstNext = pstCurr->pstNext;
                  }
               } else {
                  bAccepted = true;
                  if (++rep.c == pstCurr->u.repetition.cMax) {
                     /* Can’t match this repetition any more times; if we get back here, we’ll try
                     to match it fewer times instead. */
                     pstNext = pstCurr->pstNext;
                     rep.bBacktracking = true;
                  } else {
                     pstNext = pstCurr->u.repetition.pstRepeated;
                  }
               }
            } else {
               // New repetition: save it on the stack, and begin counting.
               vrepStack.push_back(repetition(pstCurr));
               bAccepted = true;
               pstNext = pstCurr->u.repetition.pstRepeated;
            }
            break;

         case state_type::begin:
            if (itHistory == sHistory.cbegin()) {
               bAccepted = true;
               pstNext = pstCurr->pstNext;
            }
            break;

         case state_type::end:
            if (itHistory == itHistoryEnd && itPeek == itPeekEnd) {
               /* We consumed history and peek buffer, but “end” really means end, so also check
               that the stream is empty. */
               sPeek = ptis->peek_chars(1);
               if (!sPeek) {
                  bAccepted = true;
                  pstNext = pstCurr->pstNext;
               }
            }
            break;
      }

      if (bAccepted) {
         if (!pstNext) {
            break;
         }
         // One or more states to check still; this means that we can’t accept the input just yet.
         bAccepted = false;
         vbtStack.push_back(backtrack(pstCurr->pstAlternative, bConsumedCp));
         pstCurr = pstNext;
      } else {
         // Consider the next alternative.
         pstCurr = pstCurr->pstAlternative;
         // Go back to a state that had alternatives, if possible.
         while (!pstCurr) {
            if (!vbtStack) {
               // Run out of previous states with alternatives.
               break;
            }
            backtrack bt(vbtStack.pop_back());
            pstCurr = bt.pstAlternative;
            /* If the state we’re rolling back consumed a code point, it must’ve saved it in
            sHistory. */
            if (bt.bConsumedCp) {
               --itHistory;
            }
         }
      }
   }
   return bAccepted;
}

}}} //namespace abc::text::parsers
<commit_msg>Don’t forget to advance itPeek after consuming a peeked code point<commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2016 Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
Lesser General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with Abaclade. If
not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#include <abaclade.hxx>
#include <abaclade/collections/vector.hxx>
#include <abaclade/text/parsers/dynamic.hxx>

////////////////////////////////////////////////////////////////////////////////////////////////////

namespace abc { namespace text { namespace parsers {

dynamic::state::state() {
   pstNext = nullptr;
   pstAlternative = nullptr;
   u.range.cpFirst = '\0';
   u.range.cpLast   = '\0';
   u.repetition.pstRepeated = nullptr;
   u.repetition.cMin = 0;
   u.repetition.cMax = 0;
   st = state_type::range;
}

struct dynamic::backtrack {
   backtrack(state_t const * pstAlternative_, bool bConsumedCp_) :
      pstAlternative(pstAlternative_),
      bConsumedCp(bConsumedCp_) {
   }

   state_t const * pstAlternative;
   bool bConsumedCp;
};

struct dynamic::repetition {
   explicit repetition(state_t const * pmnAnchor_) :
      pmnAnchor(pmnAnchor_),
      c(0),
      bBacktracking(false) {
   }

   state_t const * pmnAnchor;
   std::uint16_t c;
   bool bBacktracking:1;
};

dynamic::dynamic() :
   m_pstInitial(nullptr) {
}

dynamic::dynamic(dynamic && dp) :
   m_qmn(_std::move(dp.m_qmn)),
   m_pstInitial(dp.m_pstInitial) {
   dp.m_pstInitial = nullptr;
}

dynamic::~dynamic() {
}

dynamic::state * dynamic::create_state() {
   m_qmn.push_back(state_t());
   return static_cast<state *>(&m_qmn.back());
}

bool dynamic::run(str const & s) const {
   io::text::str_istream sis(external_buffer, &s);
   return run(&sis);
}

bool dynamic::run(io::text::istream * ptis) const {
   state_t const * pstCurr = m_pstInitial;
   // Setup the two sources of code points: a backtrack and a peek buffer from the input stream.
   str sPeek = ptis->peek_chars(1);
   auto itPeek(sPeek.cbegin()), itPeekEnd(sPeek.cend());
   str sHistory;
   auto itHistory(sHistory.cbegin()), itHistoryEnd(sHistory.cend());

   collections::vector<backtrack> vbtStack;
   collections::vector<repetition> vrepStack;

   // The first code point must necessarily come from sPeek, since sHistory is empty.
   bool bAccepted = false;
   while (pstCurr) {
      state_t const * pstNext = nullptr;
      bool bConsumedCp = false;
      switch (pstCurr->st) {
         case state_type::range: {
            // Get a code point from either history or the peek buffer.
            char32_t cp;
            bool bSavePeekCpToHistory;
            if (itHistory != itHistoryEnd) {
               cp = *itHistory;
               bSavePeekCpToHistory = false;
            } else {
               if (itPeek == itPeekEnd) {
                  ptis->consume_chars(sPeek.size_in_chars());
                  sPeek = ptis->peek_chars(1);
                  itPeek = sPeek.cbegin();
                  itPeekEnd = sPeek.cend();
                  if (itPeek == itPeekEnd) {
                     // Run out of code points.
                     break;
                  }
               }
               cp = *itPeek;
               bSavePeekCpToHistory = true;
            }

            if (cp >= pstCurr->u.range.cpFirst && cp <= pstCurr->u.range.cpLast) {
               bAccepted = true;
               bConsumedCp = true;
               pstNext = pstCurr->pstNext;
               if (bSavePeekCpToHistory) {
                  sHistory += cp;
                  ++itHistoryEnd;
                  ++itPeek;
               }
               ++itHistory;
            }
            break;
         }

         case state_type::repetition:
            /* TODO: this behavior is greedy; add support for non-greedy behavior, i.e. trying to
            match the least number of occurrences first. */
            if (vrepStack && vrepStack.front().pmnAnchor == pstCurr) {
               /* We just got back to the repetition at the top of the stack. Check if we should try
               to match it once more. */
               repetition & rep = vrepStack.front();
               if (rep.bBacktracking) {
                  if (rep.c-- == pstCurr->u.repetition.cMin) {
                     /* Can’t match this repetition any fewer times; this means that the repetition
                     can’t be matched. */
                     ;
                  } else {
                     /* We already know that the repetition can be matched one fewer time, and the
                     stacks already account for that; just skip straight to out of it. */
                     bAccepted = true;
                     pstNext = pstCurr->pstNext;
                  }
               } else {
                  bAccepted = true;
                  if (++rep.c == pstCurr->u.repetition.cMax) {
                     /* Can’t match this repetition any more times; if we get back here, we’ll try
                     to match it fewer times instead. */
                     pstNext = pstCurr->pstNext;
                     rep.bBacktracking = true;
                  } else {
                     pstNext = pstCurr->u.repetition.pstRepeated;
                  }
               }
            } else {
               // New repetition: save it on the stack, and begin counting.
               vrepStack.push_back(repetition(pstCurr));
               bAccepted = true;
               pstNext = pstCurr->u.repetition.pstRepeated;
            }
            break;

         case state_type::begin:
            if (itHistory == sHistory.cbegin()) {
               bAccepted = true;
               pstNext = pstCurr->pstNext;
            }
            break;

         case state_type::end:
            if (itHistory == itHistoryEnd && itPeek == itPeekEnd) {
               /* We consumed history and peek buffer, but “end” really means end, so also check
               that the stream is empty. */
               sPeek = ptis->peek_chars(1);
               if (!sPeek) {
                  bAccepted = true;
                  pstNext = pstCurr->pstNext;
               }
            }
            break;
      }

      if (bAccepted) {
         if (!pstNext) {
            break;
         }
         // One or more states to check still; this means that we can’t accept the input just yet.
         bAccepted = false;
         vbtStack.push_back(backtrack(pstCurr->pstAlternative, bConsumedCp));
         pstCurr = pstNext;
      } else {
         // Consider the next alternative.
         pstCurr = pstCurr->pstAlternative;
         // Go back to a state that had alternatives, if possible.
         while (!pstCurr) {
            if (!vbtStack) {
               // Run out of previous states with alternatives.
               break;
            }
            backtrack bt(vbtStack.pop_back());
            pstCurr = bt.pstAlternative;
            /* If the state we’re rolling back consumed a code point, it must’ve saved it in
            sHistory. */
            if (bt.bConsumedCp) {
               --itHistory;
            }
         }
      }
   }
   return bAccepted;
}

}}} //namespace abc::text::parsers
<|endoftext|>


<commit_before>#include <v8.h>
#include <node.h>
#include <node_buffer.h>
#include <string.h>
#include <assert.h>
#include "reader.h"

using namespace hiredis;

static void *tryParentize(const redisReadTask *task, const Local<Value> &v) {
    Reader *r = reinterpret_cast<Reader*>(task->privdata);
    size_t pidx, vidx;

    if (task->parent != NULL) {
        pidx = (size_t)task->parent->obj;
        assert(pidx > 0 && pidx < 3);

        /* When there is a parent, it should be an array. */
        assert(r->handle[pidx]->IsArray());
        Local<Array> parent = Local<Array>::Cast(r->handle[pidx]->ToObject());
        parent->Set(task->idx,v);

        /* Store the handle when this is an inner array. Otherwise, hiredis
         * doesn't care about the return value as long as the value is set in
         * its parent array. */
        vidx = pidx+1;
        if (v->IsArray()) {
            r->handle[vidx].Dispose();
            r->handle[vidx].Clear();
            r->handle[vidx] = Persistent<Value>::New(v);
            return (void*)vidx;
        } else {
            /* Return value doesn't matter for inner value, as long as it is
             * not NULL (which means OOM for hiredis). */
            return (void*)0xcafef00d;
        }
    } else {
        /* There is no parent, so this value is the root object. */
        r->handle[1] = Persistent<Value>::New(v);
        return (void*)1;
    }
}

static void *createArray(const redisReadTask *task, int size) {
    Local<Value> v(Array::New(size));
    return tryParentize(task,v);
}

static void *createString(const redisReadTask *task, char *str, size_t len) {
    Reader *r = reinterpret_cast<Reader*>(task->privdata);
    Local<Value> v(r->createString(str,len));

    if (task->type == REDIS_REPLY_ERROR)
        v = Exception::Error(v->ToString());
    return tryParentize(task,v);
}

static void *createInteger(const redisReadTask *task, long long value) {
    Local<Value> v(Number::New(value));
    return tryParentize(task,v);
}

static void *createNil(const redisReadTask *task) {
    Local<Value> v(Local<Value>::New(Null()));
    return tryParentize(task,v);
}

static redisReplyObjectFunctions v8ReplyFunctions = {
    createString,
    createArray,
    createInteger,
    createNil,
    NULL /* No free function: cleanup is done in Reader::Get. */
};

Reader::Reader(bool return_buffers) :
    return_buffers(return_buffers)
{
    reader = redisReaderCreate();
    reader->fn = &v8ReplyFunctions;
    reader->privdata = this;

#if NODE_VERSION_AT_LEAST(0,3,0)
    if (return_buffers) {
        Local<Object> global = Context::GetCurrent()->Global();
        Local<Value> bv = global->Get(String::NewSymbol("Buffer"));
        assert(bv->IsFunction());
        Local<Function> bf = Local<Function>::Cast(bv);
        buffer_fn = Persistent<Function>::New(bf);

        buffer_pool_length = 8*1024; /* Same as node */
        buffer_pool_offset = 0;

        Buffer *b = Buffer::New(buffer_pool_length);
        buffer_pool = Persistent<Object>::New(b->handle_);
    }
#endif
}

Reader::~Reader() {
    redisReaderFree(reader);
}

/* Don't use a HandleScope here, so the objects are created within the scope of
 * the caller (Reader::Get) and we don't have to the pay the overhead. */
Local<Value> Reader::createString(char *str, size_t len) {
    if (return_buffers) {
#if NODE_VERSION_AT_LEAST(0,3,0)
        if (len > buffer_pool_length) {
            Buffer *b = Buffer::New(str,len);
            return Local<Value>::New(b->handle_);
        } else {
            return createBufferFromPool(str,len);
        }
#else
        Buffer *b = Buffer::New(len);
        memcpy(b->data(),str,len);
        return Local<Value>::New(b->handle_);
#endif
    } else {
        return String::New(str,len);
    }
}

#if NODE_VERSION_AT_LEAST(0,3,0)
Local<Value> Reader::createBufferFromPool(char *str, size_t len) {
    HandleScope scope;
    Local<Value> argv[3];
    Local<Object> instance;

    assert(len <= buffer_pool_length);
    if (buffer_pool_length - buffer_pool_offset < len) {
        Buffer *b = Buffer::New(buffer_pool_length);
        buffer_pool.Dispose();
        buffer_pool = Persistent<Object>::New(b->handle_);
        buffer_pool_offset = 0;
    }

    memcpy(Buffer::Data(buffer_pool)+buffer_pool_offset,str,len);

    argv[0] = Local<Value>::New(buffer_pool);
    argv[1] = Integer::New(len);
    argv[2] = Integer::New(buffer_pool_offset);
    instance = buffer_fn->NewInstance(3,argv);
    buffer_pool_offset += len;
    return scope.Close(instance);
}
#endif

Handle<Value> Reader::New(const Arguments& args) {
    HandleScope scope;
    bool return_buffers = false;

    if (args.Length() > 0 && args[0]->IsObject()) {
        Local<Value> bv = args[0]->ToObject()->Get(String::New("return_buffers"));
        if (bv->IsBoolean())
            return_buffers = bv->ToBoolean()->Value();
    }

    Reader *r = new Reader(return_buffers);
    r->Wrap(args.This());
    return scope.Close(args.This());
}

void Reader::Initialize(Handle<Object> target) {
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(New);
    t->InstanceTemplate()->SetInternalFieldCount(1);
    NODE_SET_PROTOTYPE_METHOD(t, "feed", Feed);
    NODE_SET_PROTOTYPE_METHOD(t, "get", Get);
    target->Set(String::NewSymbol("Reader"), t->GetFunction());
}

Handle<Value> Reader::Feed(const Arguments &args) {
    HandleScope scope;
    Reader *r = ObjectWrap::Unwrap<Reader>(args.This());

    if (args.Length() == 0) {
        return ThrowException(Exception::Error(
            String::New("First argument must be a string or buffer")));
    } else {
        if (Buffer::HasInstance(args[0])) {
            Local<Object> buffer_object = args[0]->ToObject();
            char *data;
            size_t length;

#if NODE_VERSION_AT_LEAST(0,3,0)
            data = Buffer::Data(buffer_object);
            length = Buffer::Length(buffer_object);
#else
            Buffer *buffer = ObjectWrap::Unwrap<Buffer>(buffer_object);
            data = buffer->data();
            length = buffer->length();
#endif

            /* Can't handle OOM for now. */
            assert(redisReaderFeed(r->reader, data, length) == REDIS_OK);
        } else if (args[0]->IsString()) {
            String::Utf8Value str(args[0]->ToString());
            redisReplyReaderFeed(r->reader, *str, str.length());
        } else {
            return ThrowException(Exception::Error(
                String::New("Invalid argument")));
        }
    }

    return args.This();
}

Handle<Value> Reader::Get(const Arguments &args) {
    HandleScope scope;
    Reader *r = ObjectWrap::Unwrap<Reader>(args.This());
    void *index = NULL;
    Local<Value> reply;
    int i;

    if (redisReaderGetReply(r->reader,&index) == REDIS_OK) {
        if (index == 0) {
            return Undefined();
        } else {
            /* Complete replies should always have a root object at index 1. */
            assert((unsigned size_t)index == 1);
            reply = Local<Value>::New(r->handle[1]);

            /* Dispose and clear used handles. */
            for (i = 1; i < 3; i++) {
                r->handle[i].Dispose();
                r->handle[i].Clear();
            }
        }
    } else {
        return ThrowException(Exception::Error(String::New(r->reader->errstr)));
    }

    return scope.Close(reply);
}

<commit_msg>src: this assert() call was giving me errors for some reason...<commit_after>#include <v8.h>
#include <node.h>
#include <node_buffer.h>
#include <string.h>
#include <assert.h>
#include "reader.h"

using namespace hiredis;

static void *tryParentize(const redisReadTask *task, const Local<Value> &v) {
    Reader *r = reinterpret_cast<Reader*>(task->privdata);
    size_t pidx, vidx;

    if (task->parent != NULL) {
        pidx = (size_t)task->parent->obj;
        assert(pidx > 0 && pidx < 3);

        /* When there is a parent, it should be an array. */
        assert(r->handle[pidx]->IsArray());
        Local<Array> parent = Local<Array>::Cast(r->handle[pidx]->ToObject());
        parent->Set(task->idx,v);

        /* Store the handle when this is an inner array. Otherwise, hiredis
         * doesn't care about the return value as long as the value is set in
         * its parent array. */
        vidx = pidx+1;
        if (v->IsArray()) {
            r->handle[vidx].Dispose();
            r->handle[vidx].Clear();
            r->handle[vidx] = Persistent<Value>::New(v);
            return (void*)vidx;
        } else {
            /* Return value doesn't matter for inner value, as long as it is
             * not NULL (which means OOM for hiredis). */
            return (void*)0xcafef00d;
        }
    } else {
        /* There is no parent, so this value is the root object. */
        r->handle[1] = Persistent<Value>::New(v);
        return (void*)1;
    }
}

static void *createArray(const redisReadTask *task, int size) {
    Local<Value> v(Array::New(size));
    return tryParentize(task,v);
}

static void *createString(const redisReadTask *task, char *str, size_t len) {
    Reader *r = reinterpret_cast<Reader*>(task->privdata);
    Local<Value> v(r->createString(str,len));

    if (task->type == REDIS_REPLY_ERROR)
        v = Exception::Error(v->ToString());
    return tryParentize(task,v);
}

static void *createInteger(const redisReadTask *task, long long value) {
    Local<Value> v(Number::New(value));
    return tryParentize(task,v);
}

static void *createNil(const redisReadTask *task) {
    Local<Value> v(Local<Value>::New(Null()));
    return tryParentize(task,v);
}

static redisReplyObjectFunctions v8ReplyFunctions = {
    createString,
    createArray,
    createInteger,
    createNil,
    NULL /* No free function: cleanup is done in Reader::Get. */
};

Reader::Reader(bool return_buffers) :
    return_buffers(return_buffers)
{
    reader = redisReaderCreate();
    reader->fn = &v8ReplyFunctions;
    reader->privdata = this;

#if NODE_VERSION_AT_LEAST(0,3,0)
    if (return_buffers) {
        Local<Object> global = Context::GetCurrent()->Global();
        Local<Value> bv = global->Get(String::NewSymbol("Buffer"));
        assert(bv->IsFunction());
        Local<Function> bf = Local<Function>::Cast(bv);
        buffer_fn = Persistent<Function>::New(bf);

        buffer_pool_length = 8*1024; /* Same as node */
        buffer_pool_offset = 0;

        Buffer *b = Buffer::New(buffer_pool_length);
        buffer_pool = Persistent<Object>::New(b->handle_);
    }
#endif
}

Reader::~Reader() {
    redisReaderFree(reader);
}

/* Don't use a HandleScope here, so the objects are created within the scope of
 * the caller (Reader::Get) and we don't have to the pay the overhead. */
Local<Value> Reader::createString(char *str, size_t len) {
    if (return_buffers) {
#if NODE_VERSION_AT_LEAST(0,3,0)
        if (len > buffer_pool_length) {
            Buffer *b = Buffer::New(str,len);
            return Local<Value>::New(b->handle_);
        } else {
            return createBufferFromPool(str,len);
        }
#else
        Buffer *b = Buffer::New(len);
        memcpy(b->data(),str,len);
        return Local<Value>::New(b->handle_);
#endif
    } else {
        return String::New(str,len);
    }
}

#if NODE_VERSION_AT_LEAST(0,3,0)
Local<Value> Reader::createBufferFromPool(char *str, size_t len) {
    HandleScope scope;
    Local<Value> argv[3];
    Local<Object> instance;

    assert(len <= buffer_pool_length);
    if (buffer_pool_length - buffer_pool_offset < len) {
        Buffer *b = Buffer::New(buffer_pool_length);
        buffer_pool.Dispose();
        buffer_pool = Persistent<Object>::New(b->handle_);
        buffer_pool_offset = 0;
    }

    memcpy(Buffer::Data(buffer_pool)+buffer_pool_offset,str,len);

    argv[0] = Local<Value>::New(buffer_pool);
    argv[1] = Integer::New(len);
    argv[2] = Integer::New(buffer_pool_offset);
    instance = buffer_fn->NewInstance(3,argv);
    buffer_pool_offset += len;
    return scope.Close(instance);
}
#endif

Handle<Value> Reader::New(const Arguments& args) {
    HandleScope scope;
    bool return_buffers = false;

    if (args.Length() > 0 && args[0]->IsObject()) {
        Local<Value> bv = args[0]->ToObject()->Get(String::New("return_buffers"));
        if (bv->IsBoolean())
            return_buffers = bv->ToBoolean()->Value();
    }

    Reader *r = new Reader(return_buffers);
    r->Wrap(args.This());
    return scope.Close(args.This());
}

void Reader::Initialize(Handle<Object> target) {
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(New);
    t->InstanceTemplate()->SetInternalFieldCount(1);
    NODE_SET_PROTOTYPE_METHOD(t, "feed", Feed);
    NODE_SET_PROTOTYPE_METHOD(t, "get", Get);
    target->Set(String::NewSymbol("Reader"), t->GetFunction());
}

Handle<Value> Reader::Feed(const Arguments &args) {
    HandleScope scope;
    Reader *r = ObjectWrap::Unwrap<Reader>(args.This());

    if (args.Length() == 0) {
        return ThrowException(Exception::Error(
            String::New("First argument must be a string or buffer")));
    } else {
        if (Buffer::HasInstance(args[0])) {
            Local<Object> buffer_object = args[0]->ToObject();
            char *data;
            size_t length;

#if NODE_VERSION_AT_LEAST(0,3,0)
            data = Buffer::Data(buffer_object);
            length = Buffer::Length(buffer_object);
#else
            Buffer *buffer = ObjectWrap::Unwrap<Buffer>(buffer_object);
            data = buffer->data();
            length = buffer->length();
#endif

            /* Can't handle OOM for now. */
            assert(redisReaderFeed(r->reader, data, length) == REDIS_OK);
        } else if (args[0]->IsString()) {
            String::Utf8Value str(args[0]->ToString());
            redisReplyReaderFeed(r->reader, *str, str.length());
        } else {
            return ThrowException(Exception::Error(
                String::New("Invalid argument")));
        }
    }

    return args.This();
}

Handle<Value> Reader::Get(const Arguments &args) {
    HandleScope scope;
    Reader *r = ObjectWrap::Unwrap<Reader>(args.This());
    void *index = NULL;
    Local<Value> reply;
    int i;

    if (redisReaderGetReply(r->reader,&index) == REDIS_OK) {
        if (index == 0) {
            return Undefined();
        } else {
            /* Complete replies should always have a root object at index 1. */
            //assert((unsigned size_t)index == 1);
            reply = Local<Value>::New(r->handle[1]);

            /* Dispose and clear used handles. */
            for (i = 1; i < 3; i++) {
                r->handle[i].Dispose();
                r->handle[i].Clear();
            }
        }
    } else {
        return ThrowException(Exception::Error(String::New(r->reader->errstr)));
    }

    return scope.Close(reply);
}

<|endoftext|>


<commit_before>/*****************************************************************************
 * iteminfo.cpp : wxWindows plugin for vlc
 *****************************************************************************
 * Copyright (C) 2000-2004 VideoLAN
 * $Id: iteminfo.cpp,v 1.8 2004/01/29 17:51:08 zorglub Exp $
 *
 * Authors: Clment Stenac <zorglub@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <errno.h>                                                 /* ENOMEM */
#include <string.h>                                            /* strerror() */
#include <stdio.h>

#include <vlc/vlc.h>

#include <wx/combobox.h>
#include <wx/statline.h>

#include <vlc/intf.h>

#include "wxwindows.h"

#ifndef wxRB_SINGLE
#   define wxRB_SINGLE 0
#endif

/*****************************************************************************
 * Event Table.
 *****************************************************************************/

/* IDs for the controls and the menu commands */
enum
{
    Uri_Event,
    Name_Event,
    Author_Event,
    Enabled_Event,
    New_Event,
};

BEGIN_EVENT_TABLE(ItemInfoDialog, wxDialog)
    /* Button events */
    EVT_BUTTON(wxID_OK, ItemInfoDialog::OnOk)
    EVT_BUTTON(wxID_CANCEL, ItemInfoDialog::OnCancel)
    /* Events generated by the panels */
    EVT_BUTTON( New_Event, ItemInfoDialog::OnNewGroup)

END_EVENT_TABLE()

/*****************************************************************************
 * Constructor.
 *****************************************************************************/
ItemInfoDialog::ItemInfoDialog( intf_thread_t *_p_intf,
                                playlist_item_t *_p_item,
                                wxWindow* _p_parent ):
    wxDialog( _p_parent, -1, wxU(_("Playlist item info")),
             wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE )
{
    /* Initializations */
    p_intf = _p_intf;
    p_parent = _p_parent;
    p_item = _p_item;
    SetIcon( *p_intf->p_sys->p_icon );

    /* Create a panel to put everything in */
    wxPanel *panel = new wxPanel( this, -1 );
    panel->SetAutoLayout( TRUE );

    /* Create the standard info panel */
    wxPanel *info_panel = InfoPanel( panel );

    /* Create the group panel */
    wxPanel *group_panel = GroupPanel( panel );

    /* Separation */
    wxStaticLine *static_line = new wxStaticLine( panel, wxID_OK );

    /* Create the buttons */
    wxButton *ok_button = new wxButton( panel, wxID_OK, wxU(_("OK")) );
    ok_button->SetDefault();
    wxButton *cancel_button = new wxButton( panel, wxID_CANCEL,
                                            wxU(_("Cancel")) );

    /* Place everything in sizers */
    wxBoxSizer *button_sizer = new wxBoxSizer( wxHORIZONTAL );
    button_sizer->Add( ok_button, 0, wxALL, 5 );
    button_sizer->Add( cancel_button, 0, wxALL, 5 );
    button_sizer->Layout();
    wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
    wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
    panel_sizer->Add( info_panel, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Add( group_panel, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Add( static_line, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Add( button_sizer, 0, wxALIGN_LEFT | wxALIGN_BOTTOM |
                      wxALL, 5 );
    panel_sizer->Layout();
    panel->SetSizerAndFit( panel_sizer );
    main_sizer->Add( panel, 1, wxGROW, 0 );
    main_sizer->Layout();
    SetSizerAndFit( main_sizer );
}

ItemInfoDialog::~ItemInfoDialog()
{
}

/*****************************************************************************
 * Private methods.
 *****************************************************************************/
wxPanel *ItemInfoDialog::InfoPanel( wxWindow* parent )
{
    wxPanel *info_panel = new wxPanel( parent, -1, wxDefaultPosition,
                                  wxDefaultSize );
    info_panel->SetAutoLayout( TRUE );
    wxBoxSizer *info_sizer = new wxBoxSizer( wxHORIZONTAL );

    /* Create a box to surround the controls */
    wxStaticBox *panel_box = new wxStaticBox( info_panel, -1,
                                   wxU(_("Item Info")) );

    wxStaticBoxSizer *box_sizer = new wxStaticBoxSizer( panel_box,
                                                          wxVERTICAL );

    /* URI Textbox */
    wxStaticText *uri_label =
           new wxStaticText( info_panel, -1, wxU(_("URI")) );

    uri_text =  new wxTextCtrl( info_panel, Uri_Event,
                                wxU(p_item->psz_uri),
                                wxDefaultPosition, wxSize( 300, -1 ),
                                wxTE_PROCESS_ENTER);

    wxBoxSizer *uri_sizer = new wxBoxSizer( wxHORIZONTAL );

    uri_sizer->Add( uri_label, 0 , wxALIGN_RIGHT |wxALL , 5 );
    uri_sizer->Add( uri_text, 1 , wxALIGN_RIGHT | wxALL , 5 );
    uri_sizer->Layout();
    box_sizer->Add( uri_sizer, 1, wxEXPAND , 5);

    /* Name Textbox */
    wxStaticText *name_label =
           new wxStaticText(  info_panel, -1, wxU(_("Name")) );

    name_text =
                   new wxTextCtrl( info_panel, Uri_Event,
                                   wxU(p_item->psz_name),
                                   wxDefaultPosition, wxSize( 300, -1 ),
                                   wxTE_PROCESS_ENTER);

    wxBoxSizer *name_sizer = new wxBoxSizer( wxHORIZONTAL );

    name_sizer->Add( name_label, 0 , wxALIGN_RIGHT |wxALL , 5  );
    name_sizer->Add( name_text, 1 , wxALIGN_RIGHT | wxALL , 5 );
    name_sizer->Layout();
    box_sizer->Add( name_sizer, 1 , wxEXPAND, 5 );

    /* Author Textbox */
    wxStaticText *author_label =
           new wxStaticText( info_panel, -1, wxU(_("Author")) );

    author_text =
                   new wxTextCtrl( info_panel, Uri_Event,
                                   wxU( playlist_ItemGetInfo( p_item,
                                          _("General"), _("Author") ) ),
                                   wxDefaultPosition, wxSize( 300, -1 ),
                                   wxTE_PROCESS_ENTER);

    wxBoxSizer *author_sizer = new wxBoxSizer( wxHORIZONTAL );
    author_sizer->Add( author_label, 0 , wxALIGN_RIGHT | wxALL , 5 );
    author_sizer->Add( author_text, 1 , wxALIGN_RIGHT | wxALL , 5);
    author_sizer->Layout();
    box_sizer->Add( author_sizer, 1, wxEXPAND, 5 );

    /* Treeview */
    info_tree = new wxTreeCtrl( info_panel, -1, wxDefaultPosition,
                                wxSize(220,200),
                                wxSUNKEN_BORDER |wxTR_HAS_BUTTONS |
                                wxTR_HIDE_ROOT );

    box_sizer->Add( info_tree, 0, wxEXPAND, 5 );
    info_sizer->Add( box_sizer, 1, wxBOTTOM, 5 );

    info_panel->SetSizer( info_sizer );
    info_sizer->Layout();
    info_sizer->SetSizeHints( info_panel );

    UpdateInfo();

    return info_panel;
}

wxPanel *ItemInfoDialog::GroupPanel( wxWindow* parent )
{
    wxPanel *panel = new wxPanel( parent, -1, wxDefaultPosition,
                                  wxDefaultSize );
    wxStaticBox *panel_box = new wxStaticBox( panel, -1,
                                   wxU(_("Group Info")) );
    wxStaticBoxSizer *panel_sizer = new wxStaticBoxSizer( panel_box,
                                                         wxVERTICAL);
    wxBoxSizer *subpanel_sizer;
    group_subpanel = new wxPanel( panel, -1 );
    subpanel_sizer = new wxBoxSizer( wxVERTICAL) ;
    enabled_checkbox = new wxCheckBox( group_subpanel,
                                     -1, wxU(_("Item Enabled")) );

    enabled_checkbox->SetValue( p_item->b_enabled);

    wxStaticText *group_label = new wxStaticText( group_subpanel,
                                        -1, wxU(_("Group")) );

    playlist_t *p_playlist =
                (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                       FIND_ANYWHERE );
    if( p_playlist == NULL )
    {
        return NULL;
    }

    group_combo = new wxComboBox( group_subpanel, -1,
                                 wxT(""),wxDefaultPosition, wxDefaultSize,
                                 0, NULL,
                                 wxCB_READONLY );

    wxButton *newgroup_button = new wxButton( group_subpanel, New_Event,
                                    wxU(_("New Group")));

    for( int i=0; i< p_playlist->i_groups ; i++)
    {
        group_combo->Append( wxU( p_playlist->pp_groups[i]->psz_name ) );
    }

    vlc_object_release ( p_playlist );

    subpanel_sizer->Add( enabled_checkbox, 0, wxALIGN_RIGHT|
                         wxALIGN_CENTER_VERTICAL );
    subpanel_sizer->Add( group_label, 0, wxALIGN_LEFT |
                         wxALIGN_CENTER_VERTICAL );

    wxBoxSizer *group_sizer = new wxBoxSizer( wxHORIZONTAL);
    group_sizer->Add(group_combo, 0, wxALIGN_LEFT|wxRIGHT, 5);
    group_sizer->Add( newgroup_button, 0, wxALIGN_RIGHT|wxLEFT, 5);
    group_sizer->Layout();

    subpanel_sizer->Add( group_sizer, 0, wxALIGN_RIGHT );

    group_subpanel->SetSizerAndFit( subpanel_sizer );

    /* Stuff everything into the main panel */
    panel_sizer->Add( group_subpanel, 0,
                      wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 5 );

    panel->SetSizerAndFit( panel_sizer );

    /* Update panel */
    return panel;
}

void ItemInfoDialog::UpdateInfo()
{
    if( !info_root )
    {
       info_root = info_tree->AddRoot( wxU( p_item->psz_name) );
    }

    /* Rebuild the tree */
    for( int i = 0; i< p_item->i_categories ; i++)
    {
        if( !strcmp( p_item->pp_categories[i]->psz_name, _("Options") ) )
        {
            continue;
        }
        wxTreeItemId cat = info_tree->AppendItem( info_root,
                            wxU( p_item->pp_categories[i]->psz_name) );

        for( int j = 0 ; j < p_item->pp_categories[i]->i_infos ; j++ )
        {
           info_tree->AppendItem( cat , (wxString)
                      wxU(p_item->pp_categories[i]->pp_infos[j]->psz_name) +
                      wxT(": ") +
                      wxU(p_item->pp_categories[i]->pp_infos[j]->psz_value) );
        }
    }
}

/*****************************************************************************
 * Events methods.
 *****************************************************************************/
void ItemInfoDialog::OnOk( wxCommandEvent& WXUNUSED(event) )
{
    vlc_mutex_lock( &p_item->lock );
    p_item->psz_name = strdup( name_text->GetLineText(0).mb_str() );
    p_item->psz_uri = strdup( uri_text->GetLineText(0).mb_str() );
    playlist_ItemAddInfo( p_item,"General","Author",
                            author_text->GetLineText(0).mb_str() );
    vlc_bool_t b_old_enabled = p_item->b_enabled;

    playlist_t * p_playlist =
          (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                       FIND_ANYWHERE );
    if( p_playlist != NULL )
    {
        if( b_old_enabled == VLC_FALSE && enabled_checkbox->IsChecked() )
            p_playlist->i_enabled ++;
        else if( b_old_enabled == VLC_TRUE && !enabled_checkbox->IsChecked() )
            p_playlist->i_enabled --;

        for (int i=0 ; i< p_playlist->i_groups ; i++)
        {
           if( !strcasecmp( p_playlist->pp_groups[i]->psz_name,
                            group_combo->GetValue().mb_str() ))
           {
               p_item->i_group = p_playlist->pp_groups[i]->i_id;
               break;
           }
        }

        vlc_object_release( p_playlist );
    }

    p_item->b_enabled = enabled_checkbox->IsChecked() ? VLC_TRUE : VLC_FALSE ;
    vlc_mutex_unlock( &p_item->lock );
    EndModal( wxID_OK );
}

void ItemInfoDialog::OnCancel( wxCommandEvent& WXUNUSED(event) )
{
    EndModal( wxID_CANCEL );
}

void ItemInfoDialog::OnNewGroup( wxCommandEvent& WXUNUSED(event) )
{
    NewGroup *p_newgroup = NULL;

    p_newgroup = new NewGroup( p_intf, this );

    if( p_newgroup)
    {
        if( p_newgroup->ShowModal() == wxID_OK && p_newgroup->psz_name)
        {
            group_combo->Append( wxU( p_newgroup->psz_name ) );
        }
        delete( p_newgroup );
    }
}
<commit_msg>modules/gui/wxwindows/iteminfo.cpp:  * select the right value in the group-combobox in the iteminfo dialog<commit_after>/*****************************************************************************
 * iteminfo.cpp : wxWindows plugin for vlc
 *****************************************************************************
 * Copyright (C) 2000-2004 VideoLAN
 * $Id: iteminfo.cpp,v 1.8 2004/01/29 17:51:08 zorglub Exp $
 *
 * Authors: Clment Stenac <zorglub@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <errno.h>                                                 /* ENOMEM */
#include <string.h>                                            /* strerror() */
#include <stdio.h>

#include <vlc/vlc.h>

#include <wx/combobox.h>
#include <wx/statline.h>

#include <vlc/intf.h>

#include "wxwindows.h"

#ifndef wxRB_SINGLE
#   define wxRB_SINGLE 0
#endif

/*****************************************************************************
 * Event Table.
 *****************************************************************************/

/* IDs for the controls and the menu commands */
enum
{
    Uri_Event,
    Name_Event,
    Author_Event,
    Enabled_Event,
    New_Event,
};

BEGIN_EVENT_TABLE(ItemInfoDialog, wxDialog)
    /* Button events */
    EVT_BUTTON(wxID_OK, ItemInfoDialog::OnOk)
    EVT_BUTTON(wxID_CANCEL, ItemInfoDialog::OnCancel)
    /* Events generated by the panels */
    EVT_BUTTON( New_Event, ItemInfoDialog::OnNewGroup)

END_EVENT_TABLE()

/*****************************************************************************
 * Constructor.
 *****************************************************************************/
ItemInfoDialog::ItemInfoDialog( intf_thread_t *_p_intf,
                                playlist_item_t *_p_item,
                                wxWindow* _p_parent ):
    wxDialog( _p_parent, -1, wxU(_("Playlist item info")),
             wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE )
{
    /* Initializations */
    p_intf = _p_intf;
    p_parent = _p_parent;
    p_item = _p_item;
    SetIcon( *p_intf->p_sys->p_icon );

    /* Create a panel to put everything in */
    wxPanel *panel = new wxPanel( this, -1 );
    panel->SetAutoLayout( TRUE );

    /* Create the standard info panel */
    wxPanel *info_panel = InfoPanel( panel );

    /* Create the group panel */
    wxPanel *group_panel = GroupPanel( panel );

    /* Separation */
    wxStaticLine *static_line = new wxStaticLine( panel, wxID_OK );

    /* Create the buttons */
    wxButton *ok_button = new wxButton( panel, wxID_OK, wxU(_("OK")) );
    ok_button->SetDefault();
    wxButton *cancel_button = new wxButton( panel, wxID_CANCEL,
                                            wxU(_("Cancel")) );

    /* Place everything in sizers */
    wxBoxSizer *button_sizer = new wxBoxSizer( wxHORIZONTAL );
    button_sizer->Add( ok_button, 0, wxALL, 5 );
    button_sizer->Add( cancel_button, 0, wxALL, 5 );
    button_sizer->Layout();
    wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
    wxBoxSizer *panel_sizer = new wxBoxSizer( wxVERTICAL );
    panel_sizer->Add( info_panel, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Add( group_panel, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Add( static_line, 0, wxEXPAND | wxALL, 5 );
    panel_sizer->Add( button_sizer, 0, wxALIGN_LEFT | wxALIGN_BOTTOM |
                      wxALL, 5 );
    panel_sizer->Layout();
    panel->SetSizerAndFit( panel_sizer );
    main_sizer->Add( panel, 1, wxGROW, 0 );
    main_sizer->Layout();
    SetSizerAndFit( main_sizer );
}

ItemInfoDialog::~ItemInfoDialog()
{
}

/*****************************************************************************
 * Private methods.
 *****************************************************************************/
wxPanel *ItemInfoDialog::InfoPanel( wxWindow* parent )
{
    wxPanel *info_panel = new wxPanel( parent, -1, wxDefaultPosition,
                                  wxDefaultSize );
    info_panel->SetAutoLayout( TRUE );
    wxBoxSizer *info_sizer = new wxBoxSizer( wxHORIZONTAL );

    /* Create a box to surround the controls */
    wxStaticBox *panel_box = new wxStaticBox( info_panel, -1,
                                   wxU(_("Item Info")) );

    wxStaticBoxSizer *box_sizer = new wxStaticBoxSizer( panel_box,
                                                          wxVERTICAL );

    /* URI Textbox */
    wxStaticText *uri_label =
           new wxStaticText( info_panel, -1, wxU(_("URI")) );

    uri_text =  new wxTextCtrl( info_panel, Uri_Event,
                                wxU(p_item->psz_uri),
                                wxDefaultPosition, wxSize( 300, -1 ),
                                wxTE_PROCESS_ENTER);

    wxBoxSizer *uri_sizer = new wxBoxSizer( wxHORIZONTAL );

    uri_sizer->Add( uri_label, 0 , wxALIGN_RIGHT |wxALL , 5 );
    uri_sizer->Add( uri_text, 1 , wxALIGN_RIGHT | wxALL , 5 );
    uri_sizer->Layout();
    box_sizer->Add( uri_sizer, 1, wxEXPAND , 5);

    /* Name Textbox */
    wxStaticText *name_label =
           new wxStaticText(  info_panel, -1, wxU(_("Name")) );

    name_text =
                   new wxTextCtrl( info_panel, Uri_Event,
                                   wxU(p_item->psz_name),
                                   wxDefaultPosition, wxSize( 300, -1 ),
                                   wxTE_PROCESS_ENTER);

    wxBoxSizer *name_sizer = new wxBoxSizer( wxHORIZONTAL );

    name_sizer->Add( name_label, 0 , wxALIGN_RIGHT |wxALL , 5  );
    name_sizer->Add( name_text, 1 , wxALIGN_RIGHT | wxALL , 5 );
    name_sizer->Layout();
    box_sizer->Add( name_sizer, 1 , wxEXPAND, 5 );

    /* Author Textbox */
    wxStaticText *author_label =
           new wxStaticText( info_panel, -1, wxU(_("Author")) );

    author_text =
                   new wxTextCtrl( info_panel, Uri_Event,
                                   wxU( playlist_ItemGetInfo( p_item,
                                          _("General"), _("Author") ) ),
                                   wxDefaultPosition, wxSize( 300, -1 ),
                                   wxTE_PROCESS_ENTER);

    wxBoxSizer *author_sizer = new wxBoxSizer( wxHORIZONTAL );
    author_sizer->Add( author_label, 0 , wxALIGN_RIGHT | wxALL , 5 );
    author_sizer->Add( author_text, 1 , wxALIGN_RIGHT | wxALL , 5);
    author_sizer->Layout();
    box_sizer->Add( author_sizer, 1, wxEXPAND, 5 );

    /* Treeview */
    info_tree = new wxTreeCtrl( info_panel, -1, wxDefaultPosition,
                                wxSize(220,200),
                                wxSUNKEN_BORDER |wxTR_HAS_BUTTONS |
                                wxTR_HIDE_ROOT );

    box_sizer->Add( info_tree, 0, wxEXPAND, 5 );
    info_sizer->Add( box_sizer, 1, wxBOTTOM, 5 );

    info_panel->SetSizer( info_sizer );
    info_sizer->Layout();
    info_sizer->SetSizeHints( info_panel );

    UpdateInfo();

    return info_panel;
}

wxPanel *ItemInfoDialog::GroupPanel( wxWindow* parent )
{
    wxPanel *panel = new wxPanel( parent, -1, wxDefaultPosition,
                                  wxDefaultSize );
    wxStaticBox *panel_box = new wxStaticBox( panel, -1,
                                   wxU(_("Group Info")) );
    wxStaticBoxSizer *panel_sizer = new wxStaticBoxSizer( panel_box,
                                                         wxVERTICAL);
    wxBoxSizer *subpanel_sizer;
    group_subpanel = new wxPanel( panel, -1 );
    subpanel_sizer = new wxBoxSizer( wxVERTICAL) ;
    enabled_checkbox = new wxCheckBox( group_subpanel,
                                     -1, wxU(_("Item Enabled")) );

    enabled_checkbox->SetValue( p_item->b_enabled);

    wxStaticText *group_label = new wxStaticText( group_subpanel,
                                        -1, wxU(_("Group")) );

    playlist_t *p_playlist =
                (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                       FIND_ANYWHERE );
    if( p_playlist == NULL )
    {
        return NULL;
    }

    group_combo = new wxComboBox( group_subpanel, -1,
                                 wxT(""),wxDefaultPosition, wxDefaultSize,
                                 0, NULL,
                                 wxCB_READONLY );

    wxButton *newgroup_button = new wxButton( group_subpanel, New_Event,
                                    wxU(_("New Group")));

    for( int i=0; i< p_playlist->i_groups ; i++)
    {
        group_combo->Append( wxU( p_playlist->pp_groups[i]->psz_name ) );
        if( p_playlist->pp_groups[i]->i_id == p_item->i_group )
        {
            group_combo->SetSelection( i );
            group_combo->SetValue( wxU( p_playlist->pp_groups[i]->psz_name ) );
        }
    }

    vlc_object_release ( p_playlist );

    subpanel_sizer->Add( enabled_checkbox, 0, wxALIGN_RIGHT|
                         wxALIGN_CENTER_VERTICAL );
    subpanel_sizer->Add( group_label, 0, wxALIGN_LEFT |
                         wxALIGN_CENTER_VERTICAL );

    wxBoxSizer *group_sizer = new wxBoxSizer( wxHORIZONTAL);
    group_sizer->Add(group_combo, 0, wxALIGN_LEFT|wxRIGHT, 5);
    group_sizer->Add( newgroup_button, 0, wxALIGN_RIGHT|wxLEFT, 5);
    group_sizer->Layout();

    subpanel_sizer->Add( group_sizer, 0, wxALIGN_RIGHT );

    group_subpanel->SetSizerAndFit( subpanel_sizer );

    /* Stuff everything into the main panel */
    panel_sizer->Add( group_subpanel, 0,
                      wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 5 );

    panel->SetSizerAndFit( panel_sizer );

    /* Update panel */
    return panel;
}

void ItemInfoDialog::UpdateInfo()
{
    if( !info_root )
    {
       info_root = info_tree->AddRoot( wxU( p_item->psz_name) );
    }

    /* Rebuild the tree */
    for( int i = 0; i< p_item->i_categories ; i++)
    {
        if( !strcmp( p_item->pp_categories[i]->psz_name, _("Options") ) )
        {
            continue;
        }
        wxTreeItemId cat = info_tree->AppendItem( info_root,
                            wxU( p_item->pp_categories[i]->psz_name) );

        for( int j = 0 ; j < p_item->pp_categories[i]->i_infos ; j++ )
        {
           info_tree->AppendItem( cat , (wxString)
                      wxU(p_item->pp_categories[i]->pp_infos[j]->psz_name) +
                      wxT(": ") +
                      wxU(p_item->pp_categories[i]->pp_infos[j]->psz_value) );
        }
    }
}

/*****************************************************************************
 * Events methods.
 *****************************************************************************/
void ItemInfoDialog::OnOk( wxCommandEvent& WXUNUSED(event) )
{
    vlc_mutex_lock( &p_item->lock );
    p_item->psz_name = strdup( name_text->GetLineText(0).mb_str() );
    p_item->psz_uri = strdup( uri_text->GetLineText(0).mb_str() );
    playlist_ItemAddInfo( p_item,"General","Author",
                            author_text->GetLineText(0).mb_str() );
    vlc_bool_t b_old_enabled = p_item->b_enabled;

    playlist_t * p_playlist =
          (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                       FIND_ANYWHERE );
    if( p_playlist != NULL )
    {
        if( b_old_enabled == VLC_FALSE && enabled_checkbox->IsChecked() )
            p_playlist->i_enabled ++;
        else if( b_old_enabled == VLC_TRUE && !enabled_checkbox->IsChecked() )
            p_playlist->i_enabled --;

        for (int i=0 ; i< p_playlist->i_groups ; i++)
        {
           if( !strcasecmp( p_playlist->pp_groups[i]->psz_name,
                            group_combo->GetValue().mb_str() ))
           {
               p_item->i_group = p_playlist->pp_groups[i]->i_id;
               break;
           }
        }

        vlc_object_release( p_playlist );
    }

    p_item->b_enabled = enabled_checkbox->IsChecked() ? VLC_TRUE : VLC_FALSE ;
    vlc_mutex_unlock( &p_item->lock );
    EndModal( wxID_OK );
}

void ItemInfoDialog::OnCancel( wxCommandEvent& WXUNUSED(event) )
{
    EndModal( wxID_CANCEL );
}

void ItemInfoDialog::OnNewGroup( wxCommandEvent& WXUNUSED(event) )
{
    NewGroup *p_newgroup = NULL;

    p_newgroup = new NewGroup( p_intf, this );

    if( p_newgroup)
    {
        if( p_newgroup->ShowModal() == wxID_OK && p_newgroup->psz_name)
        {
            group_combo->Append( wxU( p_newgroup->psz_name ) );
        }
        delete( p_newgroup );
    }
}
<|endoftext|>


<commit_before><commit_msg>ant: define MaxLength struct<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/shell_dialogs.h"

#include "app/l10n_util.h"
#include "base/file_path.h"
#include "base/json/json_reader.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/dom_ui/html_dialog_ui.h"
#include "chrome/browser/shell_dialogs.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"

namespace {

static const wchar_t* kKeyNamePath = L"path";

};  // namespace

// Implementation of SelectFileDialog that shows an UI for choosing a file
// or folder using FileBrowseUI.
class SelectFileDialogImpl : public SelectFileDialog {
 public:
  explicit SelectFileDialogImpl(Listener* listener);

  // BaseShellDialog implementation.
  virtual bool IsRunning(gfx::NativeWindow parent_window) const;
  virtual void ListenerDestroyed();

  // SelectFileDialog implementation.
  // |params| is user data we pass back via the Listener interface.
  virtual void SelectFile(Type type,
                          const string16& title,
                          const FilePath& default_path,
                          const FileTypeInfo* file_types,
                          int file_type_index,
                          const FilePath::StringType& default_extension,
                          gfx::NativeWindow owning_window,
                          void* params);

 private:
  virtual ~SelectFileDialogImpl();

  class FileBrowseDelegate : public HtmlDialogUIDelegate {
   public:
    FileBrowseDelegate(SelectFileDialogImpl* owner,
                       Type type,
                       const std::wstring& title,
                       const FilePath& default_path,
                       const FileTypeInfo* file_types,
                       int file_type_index,
                       const FilePath::StringType& default_extension,
                       gfx::NativeWindow parent,
                       void* params);

    // Owner of this FileBrowseDelegate.
    SelectFileDialogImpl* owner_;

    // Parent window.
    gfx::NativeWindow parent_;

    // The type of dialog we are showing the user.
    Type type_;

    // The dialog title.
    std::wstring title_;

    // Default path of the file dialog.
    FilePath default_path_;

    // The file filters.
    FileTypeInfo file_types_;

    // The index of the default selected file filter.
    // Note: This starts from 1, not 0.
    size_t file_type_index_;

    // Default extension to be added to file if user does not type one.
    FilePath::StringType default_extension_;

    // Associated user data.
    void* params_;

   private:
    ~FileBrowseDelegate();

    // Overridden from HtmlDialogUI::Delegate:
    virtual bool IsDialogModal() const;
    virtual std::wstring GetDialogTitle() const;
    virtual GURL GetDialogContentURL() const;
    virtual void GetDOMMessageHandlers(
        std::vector<DOMMessageHandler*>* handlers) const;
    virtual void GetDialogSize(gfx::Size* size) const;
    virtual std::string GetDialogArgs() const;
    virtual void OnDialogClosed(const std::string& json_retval);

    DISALLOW_COPY_AND_ASSIGN(FileBrowseDelegate);
  };

  // Notification from FileBrowseDelegate when file browse UI is dismissed.
  void OnDialogClosed(FileBrowseDelegate* delegate, const std::string& json);

  // The set of all parent windows for which we are currently running dialogs.
  std::set<gfx::NativeWindow> parents_;

  // The set of all FileBrowseDelegate that we are currently running.
  std::set<FileBrowseDelegate*> delegates_;

  // The listener to be notified of selection completion.
  Listener* listener_;

  DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl);
};

// static
SelectFileDialog* SelectFileDialog::Create(Listener* listener) {
  DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::IO));
  DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::FILE));
  return new SelectFileDialogImpl(listener);
}

SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener)
    : listener_(listener) {
}

SelectFileDialogImpl::~SelectFileDialogImpl() {
  // All dialogs should be dismissed by now.
  DCHECK(parents_.empty() && delegates_.empty());
}

bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow parent_window) const {
  return parent_window && parents_.find(parent_window) != parents_.end();
}

void SelectFileDialogImpl::ListenerDestroyed() {
  listener_ = NULL;
}

void SelectFileDialogImpl::SelectFile(
    Type type,
    const string16& title,
    const FilePath& default_path,
    const FileTypeInfo* file_types,
    int file_type_index,
    const FilePath::StringType& default_extension,
    gfx::NativeWindow owning_window,
    void* params) {
  std::wstring title_string;
  if (title.empty()) {
    int string_id;
    switch (type) {
      case SELECT_FOLDER:
        string_id = IDS_SELECT_FOLDER_DIALOG_TITLE;
        break;
      case SELECT_OPEN_FILE:
      case SELECT_OPEN_MULTI_FILE:
        string_id = IDS_OPEN_FILE_DIALOG_TITLE;
        break;
      case SELECT_SAVEAS_FILE:
        string_id = IDS_SAVE_AS_DIALOG_TITLE;
        break;
      default:
        NOTREACHED();
        return;
    }
    title_string = l10n_util::GetString(string_id);
  } else {
    title_string = UTF16ToWide(title);
  }

  if (owning_window)
    parents_.insert(owning_window);

  FileBrowseDelegate* file_browse_delegate = new FileBrowseDelegate(this,
      type, title_string, default_path, file_types, file_type_index,
      default_extension, owning_window, params);
  delegates_.insert(file_browse_delegate);

  Browser* browser = BrowserList::GetLastActive();
  DCHECK(browser);
  browser->BrowserShowHtmlDialog(file_browse_delegate, owning_window);
}

void SelectFileDialogImpl::OnDialogClosed(FileBrowseDelegate* delegate,
                                          const std::string& json) {
  // Nothing to do if listener_ is gone.

  if (!listener_)
    return;

  bool notification_fired = false;

  if (!json.empty()) {
    scoped_ptr<Value> value(base::JSONReader::Read(json, false));
    if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) {
      // Bad json value returned.
      NOTREACHED();
    } else {
      const DictionaryValue* dict = static_cast<DictionaryValue*>(value.get());
      if (delegate->type_ == SELECT_OPEN_FILE ||
          delegate->type_ == SELECT_SAVEAS_FILE ||
          delegate->type_ == SELECT_FOLDER) {
        std::wstring path_string;
        if (dict->HasKey(kKeyNamePath) &&
            dict->GetString(kKeyNamePath, &path_string)) {
          FilePath path = FilePath::FromWStringHack(path_string);

          listener_->FileSelected(path, 0, delegate->params_);
          notification_fired = true;
        }
      } else if (delegate->type_ == SELECT_OPEN_MULTI_FILE) {
        ListValue* paths_value = NULL;
        if (dict->HasKey(kKeyNamePath) &&
            dict->GetList(kKeyNamePath, &paths_value) &&
            paths_value) {
          std::vector<FilePath> paths;
          paths.reserve(paths_value->GetSize());
          for (size_t i = 0; i < paths_value->GetSize(); ++i) {
            std::wstring path_string;
            if (paths_value->GetString(i, &path_string) &&
                !path_string.empty()) {
              paths.push_back(FilePath::FromWStringHack(path_string));
            }
          }

          listener_->MultiFilesSelected(paths, delegate->params_);
          notification_fired = true;
        }
      } else {
        NOTREACHED();
      }
    }
  }

  // Always notify listener when dialog is dismissed.
  if (!notification_fired)
    listener_->FileSelectionCanceled(delegate->params_);

  parents_.erase(delegate->parent_);
  delegates_.erase(delegate);
}

SelectFileDialogImpl::FileBrowseDelegate::FileBrowseDelegate(
    SelectFileDialogImpl* owner,
    Type type,
    const std::wstring& title,
    const FilePath& default_path,
    const FileTypeInfo* file_types,
    int file_type_index,
    const FilePath::StringType& default_extension,
    gfx::NativeWindow parent,
    void* params)
  : owner_(owner),
    parent_(parent),
    type_(type),
    title_(title),
    default_path_(default_path),
    file_type_index_(file_type_index),
    default_extension_(default_extension),
    params_(params) {
  if (file_types)
    file_types_ = *file_types;
  else
    file_types_.include_all_files = true;
}

SelectFileDialogImpl::FileBrowseDelegate::~FileBrowseDelegate() {
}

bool SelectFileDialogImpl::FileBrowseDelegate::IsDialogModal() const {
  return true;
}

std::wstring SelectFileDialogImpl::FileBrowseDelegate::GetDialogTitle() const {
  return title_;
}

GURL SelectFileDialogImpl::FileBrowseDelegate::GetDialogContentURL() const {
  std::string url_string(chrome::kChromeUIFileBrowseURL);

  return GURL(url_string);
}

void SelectFileDialogImpl::FileBrowseDelegate::GetDOMMessageHandlers(
    std::vector<DOMMessageHandler*>* handlers) const {
  return;
}

void SelectFileDialogImpl::FileBrowseDelegate::GetDialogSize(
    gfx::Size* size) const {
  size->SetSize(320, 240);
}

std::string SelectFileDialogImpl::FileBrowseDelegate::GetDialogArgs() const {
  // SelectFile inputs as json.
  //  {
  //    "type"            : "open",   // (or "open_multiple", "save", "folder"
  //    "all_files"       : true,
  //    "file_types"      : {
  //                          "exts" : [ ["htm", "html"], ["txt"] ],
  //                          "desc" : [ "HTML files", "Text files" ],
  //                        },
  //    "file_type_index" : 1,    // 1-based file type index.
  //  }
  //  See browser/shell_dialogs.h for more details.

  std::string type_string;
  switch (type_) {
    case SELECT_FOLDER:
      type_string = "folder";
      break;
    case SELECT_OPEN_FILE:
      type_string = "open";
      break;
    case SELECT_OPEN_MULTI_FILE:
      type_string = "open_multiple";
      break;
    case SELECT_SAVEAS_FILE:
      type_string = "save";
      break;
    default:
      NOTREACHED();
      return std::string();
  }

  std::string exts_list;
  std::string desc_list;
  for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
    DCHECK(!file_types_.extensions[i].empty());

    std::string exts;
    for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) {
      if (!exts.empty())
        exts.append(",");
      StringAppendF(&exts, "\"%s\"", file_types_.extensions[i][j].c_str());
    }

    if (!exts_list.empty())
      exts_list.append(",");
    StringAppendF(&exts_list, "[%s]", exts.c_str());

    std::string desc;
    if (i < file_types_.extension_description_overrides.size()) {
      desc = UTF16ToUTF8(file_types_.extension_description_overrides[i]);
    } else {
#if defined(OS_WIN)
      desc = WideToUTF8(file_types_.extensions[i][0]);
#elif defined(OS_POSIX)
      desc = file_types_.extensions[i][0];
#else
      NOTIMPLEMENTED();
#endif
    }

    if (!desc_list.empty())
      desc_list.append(",");
    StringAppendF(&desc_list, "\"%s\"", desc.c_str());
  }

  std::string filename = default_path_.BaseName().value();

  return StringPrintf("{"
        "\"type\":\"%s\","
        "\"all_files\":%s,"
        "\"current_file\":\"%s\","
        "\"file_types\":{\"exts\":[%s],\"desc\":[%s]},"
        "\"file_type_index\":%d"
      "}",
      type_string.c_str(),
      file_types_.include_all_files ? "true" : "false",
      filename.c_str(),
      exts_list.c_str(),
      desc_list.c_str(),
      file_type_index_);
}

void SelectFileDialogImpl::FileBrowseDelegate::OnDialogClosed(
    const std::string& json_retval) {
  owner_->OnDialogClosed(this, json_retval);
  delete this;
  return;
}
<commit_msg>Fix type of file_type_index_ from size_t to int to get rid of printf format warning.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/shell_dialogs.h"

#include "app/l10n_util.h"
#include "base/file_path.h"
#include "base/json/json_reader.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/dom_ui/html_dialog_ui.h"
#include "chrome/browser/shell_dialogs.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"

namespace {

static const wchar_t* kKeyNamePath = L"path";

};  // namespace

// Implementation of SelectFileDialog that shows an UI for choosing a file
// or folder using FileBrowseUI.
class SelectFileDialogImpl : public SelectFileDialog {
 public:
  explicit SelectFileDialogImpl(Listener* listener);

  // BaseShellDialog implementation.
  virtual bool IsRunning(gfx::NativeWindow parent_window) const;
  virtual void ListenerDestroyed();

  // SelectFileDialog implementation.
  // |params| is user data we pass back via the Listener interface.
  virtual void SelectFile(Type type,
                          const string16& title,
                          const FilePath& default_path,
                          const FileTypeInfo* file_types,
                          int file_type_index,
                          const FilePath::StringType& default_extension,
                          gfx::NativeWindow owning_window,
                          void* params);

 private:
  virtual ~SelectFileDialogImpl();

  class FileBrowseDelegate : public HtmlDialogUIDelegate {
   public:
    FileBrowseDelegate(SelectFileDialogImpl* owner,
                       Type type,
                       const std::wstring& title,
                       const FilePath& default_path,
                       const FileTypeInfo* file_types,
                       int file_type_index,
                       const FilePath::StringType& default_extension,
                       gfx::NativeWindow parent,
                       void* params);

    // Owner of this FileBrowseDelegate.
    SelectFileDialogImpl* owner_;

    // Parent window.
    gfx::NativeWindow parent_;

    // The type of dialog we are showing the user.
    Type type_;

    // The dialog title.
    std::wstring title_;

    // Default path of the file dialog.
    FilePath default_path_;

    // The file filters.
    FileTypeInfo file_types_;

    // The index of the default selected file filter.
    // Note: This starts from 1, not 0.
    int file_type_index_;

    // Default extension to be added to file if user does not type one.
    FilePath::StringType default_extension_;

    // Associated user data.
    void* params_;

   private:
    ~FileBrowseDelegate();

    // Overridden from HtmlDialogUI::Delegate:
    virtual bool IsDialogModal() const;
    virtual std::wstring GetDialogTitle() const;
    virtual GURL GetDialogContentURL() const;
    virtual void GetDOMMessageHandlers(
        std::vector<DOMMessageHandler*>* handlers) const;
    virtual void GetDialogSize(gfx::Size* size) const;
    virtual std::string GetDialogArgs() const;
    virtual void OnDialogClosed(const std::string& json_retval);

    DISALLOW_COPY_AND_ASSIGN(FileBrowseDelegate);
  };

  // Notification from FileBrowseDelegate when file browse UI is dismissed.
  void OnDialogClosed(FileBrowseDelegate* delegate, const std::string& json);

  // The set of all parent windows for which we are currently running dialogs.
  std::set<gfx::NativeWindow> parents_;

  // The set of all FileBrowseDelegate that we are currently running.
  std::set<FileBrowseDelegate*> delegates_;

  // The listener to be notified of selection completion.
  Listener* listener_;

  DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl);
};

// static
SelectFileDialog* SelectFileDialog::Create(Listener* listener) {
  DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::IO));
  DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::FILE));
  return new SelectFileDialogImpl(listener);
}

SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener)
    : listener_(listener) {
}

SelectFileDialogImpl::~SelectFileDialogImpl() {
  // All dialogs should be dismissed by now.
  DCHECK(parents_.empty() && delegates_.empty());
}

bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow parent_window) const {
  return parent_window && parents_.find(parent_window) != parents_.end();
}

void SelectFileDialogImpl::ListenerDestroyed() {
  listener_ = NULL;
}

void SelectFileDialogImpl::SelectFile(
    Type type,
    const string16& title,
    const FilePath& default_path,
    const FileTypeInfo* file_types,
    int file_type_index,
    const FilePath::StringType& default_extension,
    gfx::NativeWindow owning_window,
    void* params) {
  std::wstring title_string;
  if (title.empty()) {
    int string_id;
    switch (type) {
      case SELECT_FOLDER:
        string_id = IDS_SELECT_FOLDER_DIALOG_TITLE;
        break;
      case SELECT_OPEN_FILE:
      case SELECT_OPEN_MULTI_FILE:
        string_id = IDS_OPEN_FILE_DIALOG_TITLE;
        break;
      case SELECT_SAVEAS_FILE:
        string_id = IDS_SAVE_AS_DIALOG_TITLE;
        break;
      default:
        NOTREACHED();
        return;
    }
    title_string = l10n_util::GetString(string_id);
  } else {
    title_string = UTF16ToWide(title);
  }

  if (owning_window)
    parents_.insert(owning_window);

  FileBrowseDelegate* file_browse_delegate = new FileBrowseDelegate(this,
      type, title_string, default_path, file_types, file_type_index,
      default_extension, owning_window, params);
  delegates_.insert(file_browse_delegate);

  Browser* browser = BrowserList::GetLastActive();
  DCHECK(browser);
  browser->BrowserShowHtmlDialog(file_browse_delegate, owning_window);
}

void SelectFileDialogImpl::OnDialogClosed(FileBrowseDelegate* delegate,
                                          const std::string& json) {
  // Nothing to do if listener_ is gone.

  if (!listener_)
    return;

  bool notification_fired = false;

  if (!json.empty()) {
    scoped_ptr<Value> value(base::JSONReader::Read(json, false));
    if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) {
      // Bad json value returned.
      NOTREACHED();
    } else {
      const DictionaryValue* dict = static_cast<DictionaryValue*>(value.get());
      if (delegate->type_ == SELECT_OPEN_FILE ||
          delegate->type_ == SELECT_SAVEAS_FILE ||
          delegate->type_ == SELECT_FOLDER) {
        std::wstring path_string;
        if (dict->HasKey(kKeyNamePath) &&
            dict->GetString(kKeyNamePath, &path_string)) {
          FilePath path = FilePath::FromWStringHack(path_string);

          listener_->FileSelected(path, 0, delegate->params_);
          notification_fired = true;
        }
      } else if (delegate->type_ == SELECT_OPEN_MULTI_FILE) {
        ListValue* paths_value = NULL;
        if (dict->HasKey(kKeyNamePath) &&
            dict->GetList(kKeyNamePath, &paths_value) &&
            paths_value) {
          std::vector<FilePath> paths;
          paths.reserve(paths_value->GetSize());
          for (size_t i = 0; i < paths_value->GetSize(); ++i) {
            std::wstring path_string;
            if (paths_value->GetString(i, &path_string) &&
                !path_string.empty()) {
              paths.push_back(FilePath::FromWStringHack(path_string));
            }
          }

          listener_->MultiFilesSelected(paths, delegate->params_);
          notification_fired = true;
        }
      } else {
        NOTREACHED();
      }
    }
  }

  // Always notify listener when dialog is dismissed.
  if (!notification_fired)
    listener_->FileSelectionCanceled(delegate->params_);

  parents_.erase(delegate->parent_);
  delegates_.erase(delegate);
}

SelectFileDialogImpl::FileBrowseDelegate::FileBrowseDelegate(
    SelectFileDialogImpl* owner,
    Type type,
    const std::wstring& title,
    const FilePath& default_path,
    const FileTypeInfo* file_types,
    int file_type_index,
    const FilePath::StringType& default_extension,
    gfx::NativeWindow parent,
    void* params)
  : owner_(owner),
    parent_(parent),
    type_(type),
    title_(title),
    default_path_(default_path),
    file_type_index_(file_type_index),
    default_extension_(default_extension),
    params_(params) {
  if (file_types)
    file_types_ = *file_types;
  else
    file_types_.include_all_files = true;
}

SelectFileDialogImpl::FileBrowseDelegate::~FileBrowseDelegate() {
}

bool SelectFileDialogImpl::FileBrowseDelegate::IsDialogModal() const {
  return true;
}

std::wstring SelectFileDialogImpl::FileBrowseDelegate::GetDialogTitle() const {
  return title_;
}

GURL SelectFileDialogImpl::FileBrowseDelegate::GetDialogContentURL() const {
  std::string url_string(chrome::kChromeUIFileBrowseURL);

  return GURL(url_string);
}

void SelectFileDialogImpl::FileBrowseDelegate::GetDOMMessageHandlers(
    std::vector<DOMMessageHandler*>* handlers) const {
  return;
}

void SelectFileDialogImpl::FileBrowseDelegate::GetDialogSize(
    gfx::Size* size) const {
  size->SetSize(320, 240);
}

std::string SelectFileDialogImpl::FileBrowseDelegate::GetDialogArgs() const {
  // SelectFile inputs as json.
  //  {
  //    "type"            : "open",   // (or "open_multiple", "save", "folder"
  //    "all_files"       : true,
  //    "file_types"      : {
  //                          "exts" : [ ["htm", "html"], ["txt"] ],
  //                          "desc" : [ "HTML files", "Text files" ],
  //                        },
  //    "file_type_index" : 1,    // 1-based file type index.
  //  }
  //  See browser/shell_dialogs.h for more details.

  std::string type_string;
  switch (type_) {
    case SELECT_FOLDER:
      type_string = "folder";
      break;
    case SELECT_OPEN_FILE:
      type_string = "open";
      break;
    case SELECT_OPEN_MULTI_FILE:
      type_string = "open_multiple";
      break;
    case SELECT_SAVEAS_FILE:
      type_string = "save";
      break;
    default:
      NOTREACHED();
      return std::string();
  }

  std::string exts_list;
  std::string desc_list;
  for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
    DCHECK(!file_types_.extensions[i].empty());

    std::string exts;
    for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) {
      if (!exts.empty())
        exts.append(",");
      StringAppendF(&exts, "\"%s\"", file_types_.extensions[i][j].c_str());
    }

    if (!exts_list.empty())
      exts_list.append(",");
    StringAppendF(&exts_list, "[%s]", exts.c_str());

    std::string desc;
    if (i < file_types_.extension_description_overrides.size()) {
      desc = UTF16ToUTF8(file_types_.extension_description_overrides[i]);
    } else {
#if defined(OS_WIN)
      desc = WideToUTF8(file_types_.extensions[i][0]);
#elif defined(OS_POSIX)
      desc = file_types_.extensions[i][0];
#else
      NOTIMPLEMENTED();
#endif
    }

    if (!desc_list.empty())
      desc_list.append(",");
    StringAppendF(&desc_list, "\"%s\"", desc.c_str());
  }

  std::string filename = default_path_.BaseName().value();

  return StringPrintf("{"
        "\"type\":\"%s\","
        "\"all_files\":%s,"
        "\"current_file\":\"%s\","
        "\"file_types\":{\"exts\":[%s],\"desc\":[%s]},"
        "\"file_type_index\":%d"
      "}",
      type_string.c_str(),
      file_types_.include_all_files ? "true" : "false",
      filename.c_str(),
      exts_list.c_str(),
      desc_list.c_str(),
      file_type_index_);
}

void SelectFileDialogImpl::FileBrowseDelegate::OnDialogClosed(
    const std::string& json_retval) {
  owner_->OnDialogClosed(this, json_retval);
  delete this;
  return;
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Build Suite.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "qtmocscanner.h"

#include "artifact.h"
#include "productbuilddata.h"
#include "scanresultcache.h"
#include <tools/qbsassert.h>
#include <tools/scannerpluginmanager.h>
#include <tools/scripttools.h>

#include <QScriptContext>
#include <QScriptEngine>
#include <QDebug>

namespace qbs {
namespace Internal {

QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue,
        const Logger &logger)
    : m_product(product)
    , m_targetScriptValue(targetScriptValue)
    , m_logger(logger)
    , m_scanResultCache(new ScanResultCache)
    , m_cppScanner(0)
    , m_hppScanner(0)
{
    QScriptEngine *engine = targetScriptValue.engine();
    QScriptValue scannerObj = engine->newObject();
    targetScriptValue.setProperty(QLatin1String("QtMocScanner"), scannerObj);
    QScriptValue applyFunction = engine->newFunction(&js_apply, this);
    scannerObj.setProperty(QLatin1String("apply"), applyFunction);
}

QtMocScanner::~QtMocScanner()
{
    m_targetScriptValue.setProperty(QLatin1String("QtMocScanner"), QScriptValue());
    delete m_scanResultCache;
}

static ScanResultCache::Result runScanner(ScannerPlugin *scanner, const Artifact *artifact,
        ScanResultCache *scanResultCache)
{
    const QString &filepath = artifact->filePath();
    ScanResultCache::Result scanResult = scanResultCache->value(scanner, filepath);
    if (!scanResult.valid) {
        scanResult.valid = true;
        void *opaq = scanner->open(filepath.utf16(),
                                   ScanForDependenciesFlag | ScanForFileTagsFlag);
        if (!opaq || !scanner->additionalFileTags)
            return scanResult;

        int length = 0;
        const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length);
        if (szFileTagsFromScanner) {
            for (int i = length; --i >= 0;)
                scanResult.additionalFileTags += szFileTagsFromScanner[i];
        }

        QString baseDirOfInFilePath = artifact->dirPath();
        forever {
            int flags = 0;
            const char *szOutFilePath = scanner->next(opaq, &length, &flags);
            if (szOutFilePath == 0)
                break;
            QString includedFilePath = QString::fromLocal8Bit(szOutFilePath, length);
            if (includedFilePath.isEmpty())
                continue;
            bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG);
            if (isLocalInclude) {
                QString localFilePath = FileInfo::resolvePath(baseDirOfInFilePath, includedFilePath);
                if (FileInfo::exists(localFilePath))
                    includedFilePath = localFilePath;
            }
            scanResult.deps += ScanResultCache::Dependency(includedFilePath);
        }

        scanner->close(opaq);
        scanResultCache->insert(scanner, filepath, scanResult);
    }
    return scanResult;
}

void QtMocScanner::findIncludedMocCppFiles()
{
    if (!m_includedMocCppFiles.isEmpty())
        return;

    if (m_logger.traceEnabled())
        m_logger.qbsTrace() << "[QtMocScanner] looking for included moc_XXX.cpp files";

    foreach (Artifact *artifact, m_product->lookupArtifactsByFileTag("cpp")) {
        const ScanResultCache::Result scanResult
                = runScanner(m_cppScanner, artifact, m_scanResultCache);
        foreach (const ScanResultCache::Dependency &dependency, scanResult.deps) {
            QString includedFilePath = dependency.filePath();
            if (includedFilePath.startsWith("moc_") && includedFilePath.endsWith(".cpp")) {
                if (m_logger.traceEnabled())
                    m_logger.qbsTrace() << "[QtMocScanner] " << artifact->fileName()
                                        << " includes " << includedFilePath;
                includedFilePath.remove(0, 4);
                includedFilePath.chop(4);
                m_includedMocCppFiles.insert(includedFilePath, artifact->fileName());
            }
        }
    }
}

QScriptValue QtMocScanner::js_apply(QScriptContext *ctx, QScriptEngine *engine, void *data)
{
    QtMocScanner *that = reinterpret_cast<QtMocScanner *>(data);
    QScriptValue input = ctx->argument(0);
    return that->apply(engine, attachedPointer<Artifact>(input));
}

QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact)
{
    if (!m_cppScanner) {
        QList<ScannerPlugin *> scanners = ScannerPluginManager::scannersForFileTag("cpp");
        QBS_CHECK(scanners.count() == 1);
        m_cppScanner = scanners.first();
        scanners = ScannerPluginManager::scannersForFileTag("hpp");
        QBS_CHECK(scanners.count() == 1);
        m_hppScanner = scanners.first();
    }

    findIncludedMocCppFiles();

    if (m_logger.traceEnabled())
        m_logger.qbsTrace() << "[QtMocScanner] scanning " << artifact->toString();

    bool hasQObjectMacro = false;
    bool mustCompile = false;
    bool hasPluginMetaDataMacro = false;
    const bool isHeaderFile = artifact->fileTags.contains("hpp");

    ScannerPlugin * const scanner = isHeaderFile ? m_hppScanner : m_cppScanner;
    const ScanResultCache::Result scanResult = runScanner(scanner, artifact, m_scanResultCache);
    if (!scanResult.additionalFileTags.isEmpty()) {
        if (isHeaderFile) {
            if (scanResult.additionalFileTags.contains("moc_hpp"))
                hasQObjectMacro = true;
            if (scanResult.additionalFileTags.contains("moc_hpp_plugin")) {
                hasQObjectMacro = true;
                hasPluginMetaDataMacro = true;
            }
            if (!m_includedMocCppFiles.contains(FileInfo::completeBaseName(artifact->fileName())))
                mustCompile = true;
        } else {
            if (scanResult.additionalFileTags.contains("moc_cpp"))
                hasQObjectMacro = true;
        }
    }

    if (m_logger.traceEnabled()) {
        m_logger.qbsTrace() << "[QtMocScanner] hasQObjectMacro: " << hasQObjectMacro
                            << " mustCompile: " << mustCompile
                            << " hasPluginMetaDataMacro: " << hasPluginMetaDataMacro;
    }

    QScriptValue obj = engine->newObject();
    obj.setProperty(QLatin1String("hasQObjectMacro"), hasQObjectMacro);
    obj.setProperty(QLatin1String("mustCompile"), mustCompile);
    obj.setProperty(QLatin1String("hasPluginMetaDataMacro"), hasPluginMetaDataMacro);
    return obj;
}

} // namespace Internal
} // namespace qbs
<commit_msg>catch exceptions in JS callback<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Build Suite.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "qtmocscanner.h"

#include "artifact.h"
#include "productbuilddata.h"
#include "scanresultcache.h"
#include <logging/translator.h>
#include <tools/scannerpluginmanager.h>
#include <tools/scripttools.h>

#include <QScriptContext>
#include <QScriptEngine>
#include <QDebug>

namespace qbs {
namespace Internal {

QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue,
        const Logger &logger)
    : m_product(product)
    , m_targetScriptValue(targetScriptValue)
    , m_logger(logger)
    , m_scanResultCache(new ScanResultCache)
    , m_cppScanner(0)
    , m_hppScanner(0)
{
    QScriptEngine *engine = targetScriptValue.engine();
    QScriptValue scannerObj = engine->newObject();
    targetScriptValue.setProperty(QLatin1String("QtMocScanner"), scannerObj);
    QScriptValue applyFunction = engine->newFunction(&js_apply, this);
    scannerObj.setProperty(QLatin1String("apply"), applyFunction);
}

QtMocScanner::~QtMocScanner()
{
    m_targetScriptValue.setProperty(QLatin1String("QtMocScanner"), QScriptValue());
    delete m_scanResultCache;
}

static ScanResultCache::Result runScanner(ScannerPlugin *scanner, const Artifact *artifact,
        ScanResultCache *scanResultCache)
{
    const QString &filepath = artifact->filePath();
    ScanResultCache::Result scanResult = scanResultCache->value(scanner, filepath);
    if (!scanResult.valid) {
        scanResult.valid = true;
        void *opaq = scanner->open(filepath.utf16(),
                                   ScanForDependenciesFlag | ScanForFileTagsFlag);
        if (!opaq || !scanner->additionalFileTags)
            return scanResult;

        int length = 0;
        const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length);
        if (szFileTagsFromScanner) {
            for (int i = length; --i >= 0;)
                scanResult.additionalFileTags += szFileTagsFromScanner[i];
        }

        QString baseDirOfInFilePath = artifact->dirPath();
        forever {
            int flags = 0;
            const char *szOutFilePath = scanner->next(opaq, &length, &flags);
            if (szOutFilePath == 0)
                break;
            QString includedFilePath = QString::fromLocal8Bit(szOutFilePath, length);
            if (includedFilePath.isEmpty())
                continue;
            bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG);
            if (isLocalInclude) {
                QString localFilePath = FileInfo::resolvePath(baseDirOfInFilePath, includedFilePath);
                if (FileInfo::exists(localFilePath))
                    includedFilePath = localFilePath;
            }
            scanResult.deps += ScanResultCache::Dependency(includedFilePath);
        }

        scanner->close(opaq);
        scanResultCache->insert(scanner, filepath, scanResult);
    }
    return scanResult;
}

void QtMocScanner::findIncludedMocCppFiles()
{
    if (!m_includedMocCppFiles.isEmpty())
        return;

    if (m_logger.traceEnabled())
        m_logger.qbsTrace() << "[QtMocScanner] looking for included moc_XXX.cpp files";

    foreach (Artifact *artifact, m_product->lookupArtifactsByFileTag("cpp")) {
        const ScanResultCache::Result scanResult
                = runScanner(m_cppScanner, artifact, m_scanResultCache);
        foreach (const ScanResultCache::Dependency &dependency, scanResult.deps) {
            QString includedFilePath = dependency.filePath();
            if (includedFilePath.startsWith("moc_") && includedFilePath.endsWith(".cpp")) {
                if (m_logger.traceEnabled())
                    m_logger.qbsTrace() << "[QtMocScanner] " << artifact->fileName()
                                        << " includes " << includedFilePath;
                includedFilePath.remove(0, 4);
                includedFilePath.chop(4);
                m_includedMocCppFiles.insert(includedFilePath, artifact->fileName());
            }
        }
    }
}

QScriptValue QtMocScanner::js_apply(QScriptContext *ctx, QScriptEngine *engine, void *data)
{
    QtMocScanner *that = reinterpret_cast<QtMocScanner *>(data);
    QScriptValue input = ctx->argument(0);
    return that->apply(engine, attachedPointer<Artifact>(input));
}

static QScriptValue scannerCountError(QScriptEngine *engine, int scannerCount,
        const QString &fileTag)
{
    return engine->currentContext()->throwError(
                Tr::tr("There are %1 scanners for the file tag %2. "
                       "Expected is exactly one.").arg(scannerCount).arg(fileTag));
}

QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact)
{
    if (!m_cppScanner) {
        QList<ScannerPlugin *> scanners = ScannerPluginManager::scannersForFileTag("cpp");
        if (scanners.count() != 1)
            return scannerCountError(engine, scanners.count(), QLatin1String("cpp"));
        m_cppScanner = scanners.first();
        scanners = ScannerPluginManager::scannersForFileTag("hpp");
        if (scanners.count() != 1)
            return scannerCountError(engine, scanners.count(), QLatin1String("hpp"));
        m_hppScanner = scanners.first();
    }

    findIncludedMocCppFiles();

    if (m_logger.traceEnabled())
        m_logger.qbsTrace() << "[QtMocScanner] scanning " << artifact->toString();

    bool hasQObjectMacro = false;
    bool mustCompile = false;
    bool hasPluginMetaDataMacro = false;
    const bool isHeaderFile = artifact->fileTags.contains("hpp");

    ScannerPlugin * const scanner = isHeaderFile ? m_hppScanner : m_cppScanner;
    const ScanResultCache::Result scanResult = runScanner(scanner, artifact, m_scanResultCache);
    if (!scanResult.additionalFileTags.isEmpty()) {
        if (isHeaderFile) {
            if (scanResult.additionalFileTags.contains("moc_hpp"))
                hasQObjectMacro = true;
            if (scanResult.additionalFileTags.contains("moc_hpp_plugin")) {
                hasQObjectMacro = true;
                hasPluginMetaDataMacro = true;
            }
            if (!m_includedMocCppFiles.contains(FileInfo::completeBaseName(artifact->fileName())))
                mustCompile = true;
        } else {
            if (scanResult.additionalFileTags.contains("moc_cpp"))
                hasQObjectMacro = true;
        }
    }

    if (m_logger.traceEnabled()) {
        m_logger.qbsTrace() << "[QtMocScanner] hasQObjectMacro: " << hasQObjectMacro
                            << " mustCompile: " << mustCompile
                            << " hasPluginMetaDataMacro: " << hasPluginMetaDataMacro;
    }

    QScriptValue obj = engine->newObject();
    obj.setProperty(QLatin1String("hasQObjectMacro"), hasQObjectMacro);
    obj.setProperty(QLatin1String("mustCompile"), mustCompile);
    obj.setProperty(QLatin1String("hasPluginMetaDataMacro"), hasPluginMetaDataMacro);
    return obj;
}

} // namespace Internal
} // namespace qbs
<|endoftext|>